home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / unix / tkUnixWm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  144.1 KB  |  4,804 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkUnixWm.c --
  3.  *
  4.  *    This module takes care of the interactions between a Tk-based
  5.  *    application and the window manager.  Among other things, it
  6.  *    implements the "wm" command and passes geometry information
  7.  *    to the window manager.
  8.  *
  9.  * Copyright (c) 1991-1994 The Regents of the University of California.
  10.  * Copyright (c) 1994-1997 Sun Microsystems, Inc.
  11.  *
  12.  * See the file "license.terms" for information on usage and redistribution
  13.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  14.  *
  15.  * SCCS: @(#) tkUnixWm.c 1.153 97/08/13 13:41:11
  16.  */
  17.  
  18. #include "tkPort.h"
  19. #include "tkInt.h"
  20. #include "tkUnixInt.h"
  21. #include <errno.h>
  22.  
  23. /*
  24.  * A data structure of the following type holds information for
  25.  * each window manager protocol (such as WM_DELETE_WINDOW) for
  26.  * which a handler (i.e. a Tcl command) has been defined for a
  27.  * particular top-level window.
  28.  */
  29.  
  30. typedef struct ProtocolHandler {
  31.     Atom protocol;        /* Identifies the protocol. */
  32.     struct ProtocolHandler *nextPtr;
  33.                 /* Next in list of protocol handlers for
  34.                  * the same top-level window, or NULL for
  35.                  * end of list. */
  36.     Tcl_Interp *interp;        /* Interpreter in which to invoke command. */
  37.     char command[4];        /* Tcl command to invoke when a client
  38.                  * message for this protocol arrives. 
  39.                  * The actual size of the structure varies
  40.                  * to accommodate the needs of the actual
  41.                  * command. THIS MUST BE THE LAST FIELD OF
  42.                  * THE STRUCTURE. */
  43. } ProtocolHandler;
  44.  
  45. #define HANDLER_SIZE(cmdLength) \
  46.     ((unsigned) (sizeof(ProtocolHandler) - 3 + cmdLength))
  47.  
  48. /*
  49.  * A data structure of the following type holds window-manager-related
  50.  * information for each top-level window in an application.
  51.  */
  52.  
  53. typedef struct TkWmInfo {
  54.     TkWindow *winPtr;        /* Pointer to main Tk information for
  55.                  * this window. */
  56.     Window reparent;        /* If the window has been reparented, this
  57.                  * gives the ID of the ancestor of the window
  58.                  * that is a child of the root window (may
  59.                  * not be window's immediate parent).  If
  60.                  * the window isn't reparented, this has the
  61.                  * value None. */
  62.     char *title;        /* Title to display in window caption.  If
  63.                  * NULL, use name of widget.  Malloced. */
  64.     char *iconName;        /* Name to display in icon.  Malloced. */
  65.     Window master;        /* Master window for TRANSIENT_FOR property,
  66.                  * or None. */
  67.     XWMHints hints;        /* Various pieces of information for
  68.                  * window manager. */
  69.     char *leaderName;        /* Path name of leader of window group
  70.                  * (corresponds to hints.window_group).
  71.                  * Malloc-ed.  Note:  this field doesn't
  72.                  * get updated if leader is destroyed. */
  73.     char *masterWindowName;    /* Path name of window specified as master
  74.                  * in "wm transient" command, or NULL.
  75.                  * Malloc-ed. Note:  this field doesn't
  76.                  * get updated if masterWindowName is
  77.                  * destroyed. */
  78.     Tk_Window icon;        /* Window to use as icon for this window,
  79.                  * or NULL. */
  80.     Tk_Window iconFor;        /* Window for which this window is icon, or
  81.                  * NULL if this isn't an icon for anyone. */
  82.     int withdrawn;        /* Non-zero means window has been withdrawn. */
  83.  
  84.     /*
  85.      * In order to support menubars transparently under X, each toplevel
  86.      * window is encased in an additional window, called the wrapper,
  87.      * that holds the toplevel and the menubar, if any.  The information
  88.      * below is used to keep track of the wrapper and the menubar.
  89.      */
  90.  
  91.     TkWindow *wrapperPtr;    /* Pointer to information about the wrapper.
  92.                  * This is the "real" toplevel window as
  93.                  * seen by the window manager. Although
  94.                  * this is an official Tk window, it
  95.                  * doesn't appear in the application's
  96.                  * window hierarchy.  NULL means that
  97.                  * the wrapper hasn't been created yet. */
  98.     Tk_Window menubar;        /* Pointer to information about the
  99.                  * menubar, or NULL if there is no
  100.                  * menubar for this toplevel. */
  101.     int menuHeight;        /* Amount of vertical space needed for
  102.                  * menubar, measured in pixels.  If
  103.                  * menubar is non-NULL, this is >= 1 (X
  104.                  * servers don't like dimensions of 0). */
  105.  
  106.     /*
  107.      * Information used to construct an XSizeHints structure for
  108.      * the window manager:
  109.      */
  110.  
  111.     int sizeHintsFlags;        /* Flags word for XSizeHints structure.
  112.                  * If the PBaseSize flag is set then the
  113.                  * window is gridded;  otherwise it isn't
  114.                  * gridded. */
  115.     int minWidth, minHeight;    /* Minimum dimensions of window, in
  116.                  * grid units, not pixels. */
  117.     int maxWidth, maxHeight;    /* Maximum dimensions of window, in
  118.                  * grid units, not pixels. */
  119.     Tk_Window gridWin;        /* Identifies the window that controls
  120.                  * gridding for this top-level, or NULL if
  121.                  * the top-level isn't currently gridded. */
  122.     int widthInc, heightInc;    /* Increments for size changes (# pixels
  123.                  * per step). */
  124.     struct {
  125.     int x;    /* numerator */
  126.     int y;  /* denominator */
  127.     } minAspect, maxAspect;    /* Min/max aspect ratios for window. */
  128.     int reqGridWidth, reqGridHeight;
  129.                 /* The dimensions of the window (in
  130.                  * grid units) requested through
  131.                  * the geometry manager. */
  132.     int gravity;        /* Desired window gravity. */
  133.  
  134.     /*
  135.      * Information used to manage the size and location of a window.
  136.      */
  137.  
  138.     int width, height;        /* Desired dimensions of window, specified
  139.                  * in grid units.  These values are
  140.                  * set by the "wm geometry" command and by
  141.                  * ConfigureNotify events (for when wm
  142.                  * resizes window).  -1 means user hasn't
  143.                  * requested dimensions. */
  144.     int x, y;            /* Desired X and Y coordinates for window.
  145.                  * These values are set by "wm geometry",
  146.                  * plus by ConfigureNotify events (when wm
  147.                  * moves window).  These numbers are
  148.                  * different than the numbers stored in
  149.                  * winPtr->changes because (a) they could be
  150.                  * measured from the right or bottom edge
  151.                  * of the screen (see WM_NEGATIVE_X and
  152.                  * WM_NEGATIVE_Y flags) and (b) if the window
  153.                  * has been reparented then they refer to the
  154.                  * parent rather than the window itself. */
  155.     int parentWidth, parentHeight;
  156.                 /* Width and height of reparent, in pixels
  157.                  * *including border*.  If window hasn't been
  158.                  * reparented then these will be the outer
  159.                  * dimensions of the window, including
  160.                  * border. */
  161.     int xInParent, yInParent;    /* Offset of wrapperPtr within reparent,
  162.                  * measured in pixels from upper-left outer
  163.                  * corner of reparent's border to upper-left
  164.                  * outer corner of wrapperPtr's border.  If
  165.                  * not reparented then these are zero. */
  166.     int configWidth, configHeight;
  167.                 /* Dimensions passed to last request that we
  168.                  * issued to change geometry of the wrapper.
  169.                  * Used to eliminate redundant resize
  170.                  * operations. */
  171.  
  172.     /*
  173.      * Information about the virtual root window for this top-level,
  174.      * if there is one.
  175.      */
  176.  
  177.     Window vRoot;        /* Virtual root window for this top-level,
  178.                  * or None if there is no virtual root
  179.                  * window (i.e. just use the screen's root). */
  180.     int vRootX, vRootY;        /* Position of the virtual root inside the
  181.                  * root window.  If the WM_VROOT_OFFSET_STALE
  182.                  * flag is set then this information may be
  183.                  * incorrect and needs to be refreshed from
  184.                  * the X server.  If vRoot is None then these
  185.                  * values are both 0. */
  186.     int vRootWidth, vRootHeight;/* Dimensions of the virtual root window.
  187.                  * If vRoot is None, gives the dimensions
  188.                  * of the containing screen.  This information
  189.                  * is never stale, even though vRootX and
  190.                  * vRootY can be. */
  191.  
  192.     /*
  193.      * Miscellaneous information.
  194.      */
  195.  
  196.     ProtocolHandler *protPtr;    /* First in list of protocol handlers for
  197.                  * this window (NULL means none). */
  198.     int cmdArgc;        /* Number of elements in cmdArgv below. */
  199.     char **cmdArgv;        /* Array of strings to store in the
  200.                  * WM_COMMAND property.  NULL means nothing
  201.                  * available. */
  202.     char *clientMachine;    /* String to store in WM_CLIENT_MACHINE
  203.                  * property, or NULL. */
  204.     int flags;            /* Miscellaneous flags, defined below. */
  205.     struct TkWmInfo *nextPtr;    /* Next in list of all top-level windows. */
  206. } WmInfo;
  207.  
  208. /*
  209.  * Flag values for WmInfo structures:
  210.  *
  211.  * WM_NEVER_MAPPED -        non-zero means window has never been
  212.  *                mapped;  need to update all info when
  213.  *                window is first mapped.
  214.  * WM_UPDATE_PENDING -        non-zero means a call to UpdateGeometryInfo
  215.  *                has already been scheduled for this
  216.  *                window;  no need to schedule another one.
  217.  * WM_NEGATIVE_X -        non-zero means x-coordinate is measured in
  218.  *                pixels from right edge of screen, rather
  219.  *                than from left edge.
  220.  * WM_NEGATIVE_Y -        non-zero means y-coordinate is measured in
  221.  *                pixels up from bottom of screen, rather than
  222.  *                down from top.
  223.  * WM_UPDATE_SIZE_HINTS -    non-zero means that new size hints need to be
  224.  *                propagated to window manager.
  225.  * WM_SYNC_PENDING -        set to non-zero while waiting for the window
  226.  *                manager to respond to some state change.
  227.  * WM_VROOT_OFFSET_STALE -    non-zero means that (x,y) offset information
  228.  *                about the virtual root window is stale and
  229.  *                needs to be fetched fresh from the X server.
  230.  * WM_ABOUT_TO_MAP -        non-zero means that the window is about to
  231.  *                be mapped by TkWmMapWindow.  This is used
  232.  *                by UpdateGeometryInfo to modify its behavior.
  233.  * WM_MOVE_PENDING -        non-zero means the application has requested
  234.  *                a new position for the window, but it hasn't
  235.  *                been reflected through the window manager
  236.  *                yet.
  237.  * WM_COLORMAPS_EXPLICIT -    non-zero means the colormap windows were
  238.  *                set explicitly via "wm colormapwindows".
  239.  * WM_ADDED_TOPLEVEL_COLORMAP - non-zero means that when "wm colormapwindows"
  240.  *                was called the top-level itself wasn't
  241.  *                specified, so we added it implicitly at
  242.  *                the end of the list.
  243.  * WM_WIDTH_NOT_RESIZABLE -    non-zero means that we're not supposed to
  244.  *                allow the user to change the width of the
  245.  *                window (controlled by "wm resizable"
  246.  *                command).
  247.  * WM_HEIGHT_NOT_RESIZABLE -    non-zero means that we're not supposed to
  248.  *                allow the user to change the height of the
  249.  *                window (controlled by "wm resizable"
  250.  *                command).
  251.  */
  252.  
  253. #define WM_NEVER_MAPPED            1
  254. #define WM_UPDATE_PENDING        2
  255. #define WM_NEGATIVE_X            4
  256. #define WM_NEGATIVE_Y            8
  257. #define WM_UPDATE_SIZE_HINTS        0x10
  258. #define WM_SYNC_PENDING            0x20
  259. #define WM_VROOT_OFFSET_STALE        0x40
  260. #define WM_ABOUT_TO_MAP            0x100
  261. #define WM_MOVE_PENDING            0x200
  262. #define WM_COLORMAPS_EXPLICIT        0x400
  263. #define WM_ADDED_TOPLEVEL_COLORMAP    0x800
  264. #define WM_WIDTH_NOT_RESIZABLE        0x1000
  265. #define WM_HEIGHT_NOT_RESIZABLE        0x2000
  266.  
  267. /*
  268.  * This module keeps a list of all top-level windows, primarily to
  269.  * simplify the job of Tk_CoordsToWindow.
  270.  */
  271.  
  272. static WmInfo *firstWmPtr = NULL;    /* Points to first top-level window. */
  273.  
  274.  
  275. /*
  276.  * The variable below is used to enable or disable tracing in this
  277.  * module.  If tracing is enabled, then information is printed on
  278.  * standard output about interesting interactions with the window
  279.  * manager.
  280.  */
  281.  
  282. static int wmTracing = 0;
  283.  
  284. /*
  285.  * The following structures are the official type records for geometry
  286.  * management of top-level and menubar windows.
  287.  */
  288.  
  289. static void        TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
  290.                 Tk_Window tkwin));
  291.  
  292. static Tk_GeomMgr wmMgrType = {
  293.     "wm",                /* name */
  294.     TopLevelReqProc,            /* requestProc */
  295.     (Tk_GeomLostSlaveProc *) NULL,    /* lostSlaveProc */
  296. };
  297.  
  298. static void        MenubarReqProc _ANSI_ARGS_((ClientData clientData,
  299.                 Tk_Window tkwin));
  300.  
  301. static Tk_GeomMgr menubarMgrType = {
  302.     "menubar",                /* name */
  303.     MenubarReqProc,            /* requestProc */
  304.     (Tk_GeomLostSlaveProc *) NULL,    /* lostSlaveProc */
  305. };
  306.  
  307. /*
  308.  * Structures of the following type are used for communication between
  309.  * WaitForEvent, WaitRestrictProc, and WaitTimeoutProc.
  310.  */
  311.  
  312. typedef struct WaitRestrictInfo {
  313.     Display *display;        /* Window belongs to this display. */
  314.     Window window;        /* We're waiting for events on this window. */
  315.     int type;            /* We only care about this type of event. */
  316.     XEvent *eventPtr;        /* Where to store the event when it's found. */
  317.     int foundEvent;        /* Non-zero means that an event of the
  318.                  * desired type has been found. */
  319. } WaitRestrictInfo;
  320.  
  321. /*
  322.  * Forward declarations for procedures defined in this file:
  323.  */
  324.  
  325. static int        ComputeReparentGeometry _ANSI_ARGS_((WmInfo *wmPtr));
  326. static void        ConfigureEvent _ANSI_ARGS_((WmInfo *wmPtr,
  327.                 XConfigureEvent *eventPtr));
  328. static void        CreateWrapper _ANSI_ARGS_((WmInfo *wmPtr));
  329. static void        GetMaxSize _ANSI_ARGS_((WmInfo *wmPtr,
  330.                 int *maxWidthPtr, int *maxHeightPtr));
  331. static void        MenubarDestroyProc _ANSI_ARGS_((ClientData clientData,
  332.                 XEvent *eventPtr));
  333. static int        ParseGeometry _ANSI_ARGS_((Tcl_Interp *interp,
  334.                 char *string, TkWindow *winPtr));
  335. static void        ReparentEvent _ANSI_ARGS_((WmInfo *wmPtr,
  336.                 XReparentEvent *eventPtr));
  337. static void        TopLevelReqProc _ANSI_ARGS_((ClientData dummy,
  338.                 Tk_Window tkwin));
  339. static void        UpdateGeometryInfo _ANSI_ARGS_((
  340.                 ClientData clientData));
  341. static void        UpdateHints _ANSI_ARGS_((TkWindow *winPtr));
  342. static void        UpdateSizeHints _ANSI_ARGS_((TkWindow *winPtr));
  343. static void        UpdateVRootGeometry _ANSI_ARGS_((WmInfo *wmPtr));
  344. static void        UpdateWmProtocols _ANSI_ARGS_((WmInfo *wmPtr));
  345. static void        WaitForConfigureNotify _ANSI_ARGS_((TkWindow *winPtr,
  346.                 unsigned long serial));
  347. static int        WaitForEvent _ANSI_ARGS_((Display *display,
  348.                 Window window, int type, XEvent *eventPtr));
  349. static void        WaitForMapNotify _ANSI_ARGS_((TkWindow *winPtr,
  350.                 int mapped));
  351. static Tk_RestrictAction
  352.             WaitRestrictProc _ANSI_ARGS_((ClientData clientData,
  353.                 XEvent *eventPtr));
  354. static void        WrapperEventProc _ANSI_ARGS_((ClientData clientData,
  355.                 XEvent *eventPtr));
  356.  
  357. /*
  358.  *--------------------------------------------------------------
  359.  *
  360.  * TkWmNewWindow --
  361.  *
  362.  *    This procedure is invoked whenever a new top-level
  363.  *    window is created.  Its job is to initialize the WmInfo
  364.  *    structure for the window.
  365.  *
  366.  * Results:
  367.  *    None.
  368.  *
  369.  * Side effects:
  370.  *    A WmInfo structure gets allocated and initialized.
  371.  *
  372.  *--------------------------------------------------------------
  373.  */
  374.  
  375. void
  376. TkWmNewWindow(winPtr)
  377.     TkWindow *winPtr;        /* Newly-created top-level window. */
  378. {
  379.     register WmInfo *wmPtr;
  380.  
  381.     wmPtr = (WmInfo *) ckalloc(sizeof(WmInfo));
  382.     wmPtr->winPtr = winPtr;
  383.     wmPtr->reparent = None;
  384.     wmPtr->title = NULL;
  385.     wmPtr->iconName = NULL;
  386.     wmPtr->master = None;
  387.     wmPtr->hints.flags = InputHint | StateHint;
  388.     wmPtr->hints.input = True;
  389.     wmPtr->hints.initial_state = NormalState;
  390.     wmPtr->hints.icon_pixmap = None;
  391.     wmPtr->hints.icon_window = None;
  392.     wmPtr->hints.icon_x = wmPtr->hints.icon_y = 0;
  393.     wmPtr->hints.icon_mask = None;
  394.     wmPtr->hints.window_group = None;
  395.     wmPtr->leaderName = NULL;
  396.     wmPtr->masterWindowName = NULL;
  397.     wmPtr->icon = NULL;
  398.     wmPtr->iconFor = NULL;
  399.     wmPtr->withdrawn = 0;
  400.     wmPtr->wrapperPtr = NULL;
  401.     wmPtr->menubar = NULL;
  402.     wmPtr->menuHeight = 0;
  403.     wmPtr->sizeHintsFlags = 0;
  404.     wmPtr->minWidth = wmPtr->minHeight = 1;
  405.  
  406.     /*
  407.      * Default the maximum dimensions to the size of the display, minus
  408.      * a guess about how space is needed for window manager decorations.
  409.      */
  410.  
  411.     wmPtr->maxWidth = 0;
  412.     wmPtr->maxHeight = 0;
  413.     wmPtr->gridWin = NULL;
  414.     wmPtr->widthInc = wmPtr->heightInc = 1;
  415.     wmPtr->minAspect.x = wmPtr->minAspect.y = 1;
  416.     wmPtr->maxAspect.x = wmPtr->maxAspect.y = 1;
  417.     wmPtr->reqGridWidth = wmPtr->reqGridHeight = -1;
  418.     wmPtr->gravity = NorthWestGravity;
  419.     wmPtr->width = -1;
  420.     wmPtr->height = -1;
  421.     wmPtr->x = winPtr->changes.x;
  422.     wmPtr->y = winPtr->changes.y;
  423.     wmPtr->parentWidth = winPtr->changes.width
  424.         + 2*winPtr->changes.border_width;
  425.     wmPtr->parentHeight = winPtr->changes.height
  426.         + 2*winPtr->changes.border_width;
  427.     wmPtr->xInParent = wmPtr->yInParent = 0;
  428.     wmPtr->configWidth = -1;
  429.     wmPtr->configHeight = -1;
  430.     wmPtr->vRoot = None;
  431.     wmPtr->protPtr = NULL;
  432.     wmPtr->cmdArgv = NULL;
  433.     wmPtr->clientMachine = NULL;
  434.     wmPtr->flags = WM_NEVER_MAPPED;
  435.     wmPtr->nextPtr = firstWmPtr;
  436.     firstWmPtr = wmPtr;
  437.     winPtr->wmInfoPtr = wmPtr;
  438.  
  439.     UpdateVRootGeometry(wmPtr);
  440.  
  441.     /*
  442.      * Arrange for geometry requests to be reflected from the window
  443.      * to the window manager.
  444.      */
  445.  
  446.     Tk_ManageGeometry((Tk_Window) winPtr, &wmMgrType, (ClientData) 0);
  447. }
  448.  
  449. /*
  450.  *--------------------------------------------------------------
  451.  *
  452.  * TkWmMapWindow --
  453.  *
  454.  *    This procedure is invoked to map a top-level window.  This
  455.  *    module gets a chance to update all window-manager-related
  456.  *    information in properties before the window manager sees
  457.  *    the map event and checks the properties.  It also gets to
  458.  *    decide whether or not to even map the window after all.
  459.  *
  460.  * Results:
  461.  *    None.
  462.  *
  463.  * Side effects:
  464.  *    Properties of winPtr may get updated to provide up-to-date
  465.  *    information to the window manager.  The window may also get
  466.  *    mapped, but it may not be if this procedure decides that
  467.  *    isn't appropriate (e.g. because the window is withdrawn).
  468.  *
  469.  *--------------------------------------------------------------
  470.  */
  471.  
  472. void
  473. TkWmMapWindow(winPtr)
  474.     TkWindow *winPtr;        /* Top-level window that's about to
  475.                  * be mapped. */
  476. {
  477.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  478.     XTextProperty textProp;
  479.     char *string;
  480.  
  481.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  482.     wmPtr->flags &= ~WM_NEVER_MAPPED;
  483.  
  484.     /*
  485.      * This is the first time this window has ever been mapped.
  486.      * First create the wrapper window that provides space for a
  487.      * menubar.
  488.      */
  489.  
  490.     if (wmPtr->wrapperPtr == NULL) {
  491.         CreateWrapper(wmPtr);
  492.     }
  493.  
  494.     /*
  495.      * Store all the window-manager-related information for the
  496.      * window.
  497.      */
  498.  
  499.     string = (wmPtr->title != NULL) ? wmPtr->title : winPtr->nameUid;
  500.     if (XStringListToTextProperty(&string, 1, &textProp)  != 0) {
  501.         XSetWMName(winPtr->display, wmPtr->wrapperPtr->window, &textProp);
  502.         XFree((char *) textProp.value);
  503.     }
  504.     
  505.     TkWmSetClass(winPtr);
  506.  
  507.     if (wmPtr->iconName != NULL) {
  508.         XSetIconName(winPtr->display, wmPtr->wrapperPtr->window,
  509.             wmPtr->iconName);
  510.     }
  511.     
  512.     if (wmPtr->master != None) {
  513.         XSetTransientForHint(winPtr->display, wmPtr->wrapperPtr->window,
  514.             wmPtr->master);
  515.     }
  516.     
  517.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  518.     UpdateHints(winPtr);
  519.     UpdateWmProtocols(wmPtr);
  520.     if (wmPtr->cmdArgv != NULL) {
  521.         XSetCommand(winPtr->display, wmPtr->wrapperPtr->window,
  522.             wmPtr->cmdArgv, wmPtr->cmdArgc);
  523.     }
  524.     if (wmPtr->clientMachine != NULL) {
  525.         if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
  526.             != 0) {
  527.         XSetWMClientMachine(winPtr->display, wmPtr->wrapperPtr->window,
  528.             &textProp);
  529.         XFree((char *) textProp.value);
  530.         }
  531.     }
  532.     }
  533.     if (wmPtr->hints.initial_state == WithdrawnState) {
  534.     return;
  535.     }
  536.     if (wmPtr->iconFor != NULL) {
  537.     /*
  538.      * This window is an icon for somebody else.  Make sure that
  539.      * the geometry is up-to-date, then return without mapping
  540.      * the window.
  541.      */
  542.  
  543.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  544.         Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  545.     }
  546.     UpdateGeometryInfo((ClientData) winPtr);
  547.     return;
  548.     }
  549.     wmPtr->flags |= WM_ABOUT_TO_MAP;
  550.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  551.     Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  552.     }
  553.     UpdateGeometryInfo((ClientData) winPtr);
  554.     wmPtr->flags &= ~WM_ABOUT_TO_MAP;
  555.  
  556.     /*
  557.      * Map the window, then wait to be sure that the window manager has
  558.      * processed the map operation.
  559.      */
  560.  
  561.     XMapWindow(winPtr->display, wmPtr->wrapperPtr->window);
  562.     if (wmPtr->hints.initial_state == NormalState) {
  563.     WaitForMapNotify(winPtr, 1);
  564.     }
  565. }
  566.  
  567. /*
  568.  *--------------------------------------------------------------
  569.  *
  570.  * TkWmUnmapWindow --
  571.  *
  572.  *    This procedure is invoked to unmap a top-level window.  The
  573.  *    only thing it does special is to wait for the window actually
  574.  *    to be unmapped.
  575.  *
  576.  * Results:
  577.  *    None.
  578.  *
  579.  * Side effects:
  580.  *    Unmaps the window.
  581.  *
  582.  *--------------------------------------------------------------
  583.  */
  584.  
  585. void
  586. TkWmUnmapWindow(winPtr)
  587.     TkWindow *winPtr;        /* Top-level window that's about to
  588.                  * be mapped. */
  589. {
  590.     /*
  591.      * It seems to be important to wait after unmapping a top-level
  592.      * window until the window really gets unmapped.  I don't completely
  593.      * understand all the interactions with the window manager, but if
  594.      * we go on without waiting, and if the window is then mapped again
  595.      * quickly, events seem to get lost so that we think the window isn't
  596.      * mapped when in fact it is mapped.  I suspect that this has something
  597.      * to do with the window manager filtering Map events (and possily not
  598.      * filtering Unmap events?).
  599.      */ 
  600.     XUnmapWindow(winPtr->display, winPtr->wmInfoPtr->wrapperPtr->window);
  601.     WaitForMapNotify(winPtr, 0);
  602. }
  603.  
  604. /*
  605.  *--------------------------------------------------------------
  606.  *
  607.  * TkWmDeadWindow --
  608.  *
  609.  *    This procedure is invoked when a top-level window is
  610.  *    about to be deleted.  It cleans up the wm-related data
  611.  *    structures for the window.
  612.  *
  613.  * Results:
  614.  *    None.
  615.  *
  616.  * Side effects:
  617.  *    The WmInfo structure for winPtr gets freed up.
  618.  *
  619.  *--------------------------------------------------------------
  620.  */
  621.  
  622. void
  623. TkWmDeadWindow(winPtr)
  624.     TkWindow *winPtr;        /* Top-level window that's being deleted. */
  625. {
  626.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  627.     WmInfo *wmPtr2;
  628.  
  629.     if (wmPtr == NULL) {
  630.     return;
  631.     }
  632.     if (firstWmPtr == wmPtr) {
  633.     firstWmPtr = wmPtr->nextPtr;
  634.     } else {
  635.     register WmInfo *prevPtr;
  636.  
  637.     for (prevPtr = firstWmPtr; ; prevPtr = prevPtr->nextPtr) {
  638.         if (prevPtr == NULL) {
  639.         panic("couldn't unlink window in TkWmDeadWindow");
  640.         }
  641.         if (prevPtr->nextPtr == wmPtr) {
  642.         prevPtr->nextPtr = wmPtr->nextPtr;
  643.         break;
  644.         }
  645.     }
  646.     }
  647.     if (wmPtr->title != NULL) {
  648.     ckfree(wmPtr->title);
  649.     }
  650.     if (wmPtr->iconName != NULL) {
  651.     ckfree(wmPtr->iconName);
  652.     }
  653.     if (wmPtr->hints.flags & IconPixmapHint) {
  654.     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
  655.     }
  656.     if (wmPtr->hints.flags & IconMaskHint) {
  657.     Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
  658.     }
  659.     if (wmPtr->leaderName != NULL) {
  660.     ckfree(wmPtr->leaderName);
  661.     }
  662.     if (wmPtr->masterWindowName != NULL) {
  663.     ckfree(wmPtr->masterWindowName);
  664.     }
  665.     if (wmPtr->icon != NULL) {
  666.     wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
  667.     wmPtr2->iconFor = NULL;
  668.     wmPtr2->withdrawn = 1;
  669.     }
  670.     if (wmPtr->iconFor != NULL) {
  671.     wmPtr2 = ((TkWindow *) wmPtr->iconFor)->wmInfoPtr;
  672.     wmPtr2->icon = NULL;
  673.     wmPtr2->hints.flags &= ~IconWindowHint;
  674.     UpdateHints((TkWindow *) wmPtr->iconFor);
  675.     }
  676.     if (wmPtr->menubar != NULL) {
  677.     Tk_DestroyWindow(wmPtr->menubar);
  678.     }
  679.     if (wmPtr->wrapperPtr != NULL) {
  680.     /*
  681.      * The rest of Tk doesn't know that we reparent the toplevel
  682.      * inside the wrapper, so reparent it back out again before
  683.      * deleting the wrapper; otherwise the toplevel will get deleted
  684.      * twice (once implicitly by the deletion of the wrapper).
  685.      */
  686.  
  687.     XUnmapWindow(winPtr->display, winPtr->window);
  688.     XReparentWindow(winPtr->display, winPtr->window,
  689.         XRootWindow(winPtr->display, winPtr->screenNum), 0, 0);
  690.     Tk_DestroyWindow((Tk_Window) wmPtr->wrapperPtr);
  691.     }
  692.     while (wmPtr->protPtr != NULL) {
  693.     ProtocolHandler *protPtr;
  694.  
  695.     protPtr = wmPtr->protPtr;
  696.     wmPtr->protPtr = protPtr->nextPtr;
  697.     Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
  698.     }
  699.     if (wmPtr->cmdArgv != NULL) {
  700.     ckfree((char *) wmPtr->cmdArgv);
  701.     }
  702.     if (wmPtr->clientMachine != NULL) {
  703.     ckfree((char *) wmPtr->clientMachine);
  704.     }
  705.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  706.     Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  707.     }
  708.     ckfree((char *) wmPtr);
  709.     winPtr->wmInfoPtr = NULL;
  710. }
  711.  
  712. /*
  713.  *--------------------------------------------------------------
  714.  *
  715.  * TkWmSetClass --
  716.  *
  717.  *    This procedure is invoked whenever a top-level window's
  718.  *    class is changed.  If the window has been mapped then this
  719.  *    procedure updates the window manager property for the
  720.  *    class.  If the window hasn't been mapped, the update is
  721.  *    deferred until just before the first mapping.
  722.  *
  723.  * Results:
  724.  *    None.
  725.  *
  726.  * Side effects:
  727.  *    A window property may get updated.
  728.  *
  729.  *--------------------------------------------------------------
  730.  */
  731.  
  732. void
  733. TkWmSetClass(winPtr)
  734.     TkWindow *winPtr;        /* Newly-created top-level window. */
  735. {
  736.     if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
  737.     return;
  738.     }
  739.  
  740.     if (winPtr->classUid != NULL) {
  741.     XClassHint *classPtr;
  742.  
  743.     classPtr = XAllocClassHint();
  744.     classPtr->res_name = winPtr->nameUid;
  745.     classPtr->res_class = winPtr->classUid;
  746.     XSetClassHint(winPtr->display, winPtr->wmInfoPtr->wrapperPtr->window,
  747.         classPtr);
  748.     XFree((char *) classPtr);
  749.     }
  750. }
  751.  
  752. /*
  753.  *----------------------------------------------------------------------
  754.  *
  755.  * Tk_WmCmd --
  756.  *
  757.  *    This procedure is invoked to process the "wm" Tcl command.
  758.  *    See the user documentation for details on what it does.
  759.  *
  760.  * Results:
  761.  *    A standard Tcl result.
  762.  *
  763.  * Side effects:
  764.  *    See the user documentation.
  765.  *
  766.  *----------------------------------------------------------------------
  767.  */
  768.  
  769.     /* ARGSUSED */
  770. int
  771. Tk_WmCmd(clientData, interp, argc, argv)
  772.     ClientData clientData;    /* Main window associated with
  773.                  * interpreter. */
  774.     Tcl_Interp *interp;        /* Current interpreter. */
  775.     int argc;            /* Number of arguments. */
  776.     char **argv;        /* Argument strings. */
  777. {
  778.     Tk_Window tkwin = (Tk_Window) clientData;
  779.     TkWindow *winPtr;
  780.     register WmInfo *wmPtr;
  781.     int c;
  782.     size_t length;
  783.  
  784.     if (argc < 2) {
  785.     wrongNumArgs:
  786.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  787.         argv[0], " option window ?arg ...?\"", (char *) NULL);
  788.     return TCL_ERROR;
  789.     }
  790.     c = argv[1][0];
  791.     length = strlen(argv[1]);
  792.     if ((c == 't') && (strncmp(argv[1], "tracing", length) == 0)
  793.         && (length >= 3)) {
  794.     if ((argc != 2) && (argc != 3)) {
  795.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  796.             argv[0], " tracing ?boolean?\"", (char *) NULL);
  797.         return TCL_ERROR;
  798.     }
  799.     if (argc == 2) {
  800.         interp->result = (wmTracing) ? "on" : "off";
  801.         return TCL_OK;
  802.     }
  803.     return Tcl_GetBoolean(interp, argv[2], &wmTracing);
  804.     }
  805.  
  806.     if (argc < 3) {
  807.     goto wrongNumArgs;
  808.     }
  809.     winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], tkwin);
  810.     if (winPtr == NULL) {
  811.     return TCL_ERROR;
  812.     }
  813.     if (!(winPtr->flags & TK_TOP_LEVEL)) {
  814.     Tcl_AppendResult(interp, "window \"", winPtr->pathName,
  815.         "\" isn't a top-level window", (char *) NULL);
  816.     return TCL_ERROR;
  817.     }
  818.     wmPtr = winPtr->wmInfoPtr;
  819.     if ((c == 'a') && (strncmp(argv[1], "aspect", length) == 0)) {
  820.     int numer1, denom1, numer2, denom2;
  821.  
  822.     if ((argc != 3) && (argc != 7)) {
  823.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  824.             argv[0], " aspect window ?minNumer minDenom ",
  825.             "maxNumer maxDenom?\"", (char *) NULL);
  826.         return TCL_ERROR;
  827.     }
  828.     if (argc == 3) {
  829.         if (wmPtr->sizeHintsFlags & PAspect) {
  830.         sprintf(interp->result, "%d %d %d %d", wmPtr->minAspect.x,
  831.             wmPtr->minAspect.y, wmPtr->maxAspect.x,
  832.             wmPtr->maxAspect.y);
  833.         }
  834.         return TCL_OK;
  835.     }
  836.     if (*argv[3] == '\0') {
  837.         wmPtr->sizeHintsFlags &= ~PAspect;
  838.     } else {
  839.         if ((Tcl_GetInt(interp, argv[3], &numer1) != TCL_OK)
  840.             || (Tcl_GetInt(interp, argv[4], &denom1) != TCL_OK)
  841.             || (Tcl_GetInt(interp, argv[5], &numer2) != TCL_OK)
  842.             || (Tcl_GetInt(interp, argv[6], &denom2) != TCL_OK)) {
  843.         return TCL_ERROR;
  844.         }
  845.         if ((numer1 <= 0) || (denom1 <= 0) || (numer2 <= 0) ||
  846.             (denom2 <= 0)) {
  847.         interp->result = "aspect number can't be <= 0";
  848.         return TCL_ERROR;
  849.         }
  850.         wmPtr->minAspect.x = numer1;
  851.         wmPtr->minAspect.y = denom1;
  852.         wmPtr->maxAspect.x = numer2;
  853.         wmPtr->maxAspect.y = denom2;
  854.         wmPtr->sizeHintsFlags |= PAspect;
  855.     }
  856.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  857.     goto updateGeom;
  858.     } else if ((c == 'c') && (strncmp(argv[1], "client", length) == 0)
  859.         && (length >= 2)) {
  860.     if ((argc != 3) && (argc != 4)) {
  861.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  862.             argv[0], " client window ?name?\"",
  863.             (char *) NULL);
  864.         return TCL_ERROR;
  865.     }
  866.     if (argc == 3) {
  867.         if (wmPtr->clientMachine != NULL) {
  868.         interp->result = wmPtr->clientMachine;
  869.         }
  870.         return TCL_OK;
  871.     }
  872.     if (argv[3][0] == 0) {
  873.         if (wmPtr->clientMachine != NULL) {
  874.         ckfree((char *) wmPtr->clientMachine);
  875.         wmPtr->clientMachine = NULL;
  876.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  877.             XDeleteProperty(winPtr->display, wmPtr->wrapperPtr->window,
  878.                 Tk_InternAtom((Tk_Window) winPtr,
  879.                 "WM_CLIENT_MACHINE"));
  880.         }
  881.         }
  882.         return TCL_OK;
  883.     }
  884.     if (wmPtr->clientMachine != NULL) {
  885.         ckfree((char *) wmPtr->clientMachine);
  886.     }
  887.     wmPtr->clientMachine = (char *)
  888.         ckalloc((unsigned) (strlen(argv[3]) + 1));
  889.     strcpy(wmPtr->clientMachine, argv[3]);
  890.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  891.         XTextProperty textProp;
  892.         if (XStringListToTextProperty(&wmPtr->clientMachine, 1, &textProp)
  893.             != 0) {
  894.         XSetWMClientMachine(winPtr->display, wmPtr->wrapperPtr->window,
  895.             &textProp);
  896.         XFree((char *) textProp.value);
  897.         }
  898.     }
  899.     } else if ((c == 'c') && (strncmp(argv[1], "colormapwindows", length) == 0)
  900.         && (length >= 3)) {
  901.     Window *cmapList;
  902.     TkWindow *winPtr2;
  903.     int count, i, windowArgc, gotToplevel;
  904.     char buffer[20], **windowArgv;
  905.  
  906.     if ((argc != 3) && (argc != 4)) {
  907.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  908.             argv[0], " colormapwindows window ?windowList?\"",
  909.             (char *) NULL);
  910.         return TCL_ERROR;
  911.     }
  912.     Tk_MakeWindowExist((Tk_Window) winPtr);
  913.     if (wmPtr->wrapperPtr == NULL) {
  914.         CreateWrapper(wmPtr);
  915.     }
  916.     if (argc == 3) {
  917.         if (XGetWMColormapWindows(winPtr->display,
  918.             wmPtr->wrapperPtr->window, &cmapList, &count) == 0) {
  919.         return TCL_OK;
  920.         }
  921.         for (i = 0; i < count; i++) {
  922.         if ((i == (count-1))
  923.             && (wmPtr->flags & WM_ADDED_TOPLEVEL_COLORMAP)) {
  924.             break;
  925.         }
  926.             winPtr2  = (TkWindow *) Tk_IdToWindow(winPtr->display,
  927.             cmapList[i]);
  928.         if (winPtr2 == NULL) {
  929.             sprintf(buffer, "0x%lx", cmapList[i]);
  930.             Tcl_AppendElement(interp, buffer);
  931.         } else {
  932.             Tcl_AppendElement(interp, winPtr2->pathName);
  933.         }
  934.         }
  935.         XFree((char *) cmapList);
  936.         return TCL_OK;
  937.     }
  938.     if (Tcl_SplitList(interp, argv[3], &windowArgc, &windowArgv)
  939.         != TCL_OK) {
  940.         return TCL_ERROR;
  941.     }
  942.     cmapList = (Window *) ckalloc((unsigned)
  943.         ((windowArgc+1)*sizeof(Window)));
  944.     gotToplevel = 0;
  945.     for (i = 0; i < windowArgc; i++) {
  946.         winPtr2 = (TkWindow *) Tk_NameToWindow(interp, windowArgv[i],
  947.             tkwin);
  948.         if (winPtr2 == NULL) {
  949.         ckfree((char *) cmapList);
  950.         ckfree((char *) windowArgv);
  951.         return TCL_ERROR;
  952.         }
  953.         if (winPtr2 == winPtr) {
  954.         gotToplevel = 1;
  955.         }
  956.         if (winPtr2->window == None) {
  957.         Tk_MakeWindowExist((Tk_Window) winPtr2);
  958.         }
  959.         cmapList[i] = winPtr2->window;
  960.     }
  961.     if (!gotToplevel) {
  962.         wmPtr->flags |= WM_ADDED_TOPLEVEL_COLORMAP;
  963.         cmapList[windowArgc] = wmPtr->wrapperPtr->window;
  964.         windowArgc++;
  965.     } else {
  966.         wmPtr->flags &= ~WM_ADDED_TOPLEVEL_COLORMAP;
  967.     }
  968.     wmPtr->flags |= WM_COLORMAPS_EXPLICIT;
  969.     XSetWMColormapWindows(winPtr->display, wmPtr->wrapperPtr->window,
  970.         cmapList, windowArgc);
  971.     ckfree((char *) cmapList);
  972.     ckfree((char *) windowArgv);
  973.     return TCL_OK;
  974.     } else if ((c == 'c') && (strncmp(argv[1], "command", length) == 0)
  975.         && (length >= 3)) {
  976.     int cmdArgc;
  977.     char **cmdArgv;
  978.  
  979.     if ((argc != 3) && (argc != 4)) {
  980.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  981.             argv[0], " command window ?value?\"",
  982.             (char *) NULL);
  983.         return TCL_ERROR;
  984.     }
  985.     if (argc == 3) {
  986.         if (wmPtr->cmdArgv != NULL) {
  987.         interp->result = Tcl_Merge(wmPtr->cmdArgc, wmPtr->cmdArgv);
  988.         interp->freeProc = TCL_DYNAMIC;
  989.         }
  990.         return TCL_OK;
  991.     }
  992.     if (argv[3][0] == 0) {
  993.         if (wmPtr->cmdArgv != NULL) {
  994.         ckfree((char *) wmPtr->cmdArgv);
  995.         wmPtr->cmdArgv = NULL;
  996.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  997.             XDeleteProperty(winPtr->display, wmPtr->wrapperPtr->window,
  998.                 Tk_InternAtom((Tk_Window) winPtr, "WM_COMMAND"));
  999.         }
  1000.         }
  1001.         return TCL_OK;
  1002.     }
  1003.     if (Tcl_SplitList(interp, argv[3], &cmdArgc, &cmdArgv) != TCL_OK) {
  1004.         return TCL_ERROR;
  1005.     }
  1006.     if (wmPtr->cmdArgv != NULL) {
  1007.         ckfree((char *) wmPtr->cmdArgv);
  1008.     }
  1009.     wmPtr->cmdArgc = cmdArgc;
  1010.     wmPtr->cmdArgv = cmdArgv;
  1011.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1012.         XSetCommand(winPtr->display, wmPtr->wrapperPtr->window,
  1013.             cmdArgv, cmdArgc);
  1014.     }
  1015.     } else if ((c == 'd') && (strncmp(argv[1], "deiconify", length) == 0)) {
  1016.     if (argc != 3) {
  1017.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1018.             argv[0], " deiconify window\"", (char *) NULL);
  1019.         return TCL_ERROR;
  1020.     }
  1021.     if (wmPtr->iconFor != NULL) {
  1022.         Tcl_AppendResult(interp, "can't deiconify ", argv[2],
  1023.             ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
  1024.             (char *) NULL);
  1025.         return TCL_ERROR;
  1026.     }
  1027.     wmPtr->hints.initial_state = NormalState;
  1028.     wmPtr->withdrawn = 0;
  1029.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  1030.         return TCL_OK;
  1031.     }
  1032.     UpdateHints(winPtr);
  1033.     Tk_MapWindow((Tk_Window) winPtr);
  1034.     } else if ((c == 'f') && (strncmp(argv[1], "focusmodel", length) == 0)
  1035.         && (length >= 2)) {
  1036.     if ((argc != 3) && (argc != 4)) {
  1037.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1038.             argv[0], " focusmodel window ?active|passive?\"",
  1039.             (char *) NULL);
  1040.         return TCL_ERROR;
  1041.     }
  1042.     if (argc == 3) {
  1043.         interp->result = wmPtr->hints.input ? "passive" : "active";
  1044.         return TCL_OK;
  1045.     }
  1046.     c = argv[3][0];
  1047.     length = strlen(argv[3]);
  1048.     if ((c == 'a') && (strncmp(argv[3], "active", length) == 0)) {
  1049.         wmPtr->hints.input = False;
  1050.     } else if ((c == 'p') && (strncmp(argv[3], "passive", length) == 0)) {
  1051.         wmPtr->hints.input = True;
  1052.     } else {
  1053.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  1054.             "\": must be active or passive", (char *) NULL);
  1055.         return TCL_ERROR;
  1056.     }
  1057.     UpdateHints(winPtr);
  1058.     } else if ((c == 'f') && (strncmp(argv[1], "frame", length) == 0)
  1059.         && (length >= 2)) {
  1060.     Window window;
  1061.  
  1062.     if (argc != 3) {
  1063.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1064.             argv[0], " frame window\"", (char *) NULL);
  1065.         return TCL_ERROR;
  1066.     }
  1067.     window = wmPtr->reparent;
  1068.     if (window == None) {
  1069.         window = Tk_WindowId((Tk_Window) winPtr);
  1070.     }
  1071.     sprintf(interp->result, "0x%x", (unsigned int) window);
  1072.     } else if ((c == 'g') && (strncmp(argv[1], "geometry", length) == 0)
  1073.         && (length >= 2)) {
  1074.     char xSign, ySign;
  1075.     int width, height;
  1076.  
  1077.     if ((argc != 3) && (argc != 4)) {
  1078.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1079.             argv[0], " geometry window ?newGeometry?\"",
  1080.             (char *) NULL);
  1081.         return TCL_ERROR;
  1082.     }
  1083.     if (argc == 3) {
  1084.         xSign = (wmPtr->flags & WM_NEGATIVE_X) ? '-' : '+';
  1085.         ySign = (wmPtr->flags & WM_NEGATIVE_Y) ? '-' : '+';
  1086.         if (wmPtr->gridWin != NULL) {
  1087.         width = wmPtr->reqGridWidth + (winPtr->changes.width
  1088.             - winPtr->reqWidth)/wmPtr->widthInc;
  1089.         height = wmPtr->reqGridHeight + (winPtr->changes.height
  1090.             - winPtr->reqHeight)/wmPtr->heightInc;
  1091.         } else {
  1092.         width = winPtr->changes.width;
  1093.         height = winPtr->changes.height;
  1094.         }
  1095.         sprintf(interp->result, "%dx%d%c%d%c%d", width, height,
  1096.             xSign, wmPtr->x, ySign, wmPtr->y);
  1097.         return TCL_OK;
  1098.     }
  1099.     if (*argv[3] == '\0') {
  1100.         wmPtr->width = -1;
  1101.         wmPtr->height = -1;
  1102.         goto updateGeom;
  1103.     }
  1104.     return ParseGeometry(interp, argv[3], winPtr);
  1105.     } else if ((c == 'g') && (strncmp(argv[1], "grid", length) == 0)
  1106.         && (length >= 3)) {
  1107.     int reqWidth, reqHeight, widthInc, heightInc;
  1108.  
  1109.     if ((argc != 3) && (argc != 7)) {
  1110.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1111.             argv[0], " grid window ?baseWidth baseHeight ",
  1112.             "widthInc heightInc?\"", (char *) NULL);
  1113.         return TCL_ERROR;
  1114.     }
  1115.     if (argc == 3) {
  1116.         if (wmPtr->sizeHintsFlags & PBaseSize) {
  1117.         sprintf(interp->result, "%d %d %d %d", wmPtr->reqGridWidth,
  1118.             wmPtr->reqGridHeight, wmPtr->widthInc,
  1119.             wmPtr->heightInc);
  1120.         }
  1121.         return TCL_OK;
  1122.     }
  1123.     if (*argv[3] == '\0') {
  1124.         /*
  1125.          * Turn off gridding and reset the width and height
  1126.          * to make sense as ungridded numbers.
  1127.          */
  1128.  
  1129.         wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
  1130.         if (wmPtr->width != -1) {
  1131.         wmPtr->width = winPtr->reqWidth + (wmPtr->width
  1132.             - wmPtr->reqGridWidth)*wmPtr->widthInc;
  1133.         wmPtr->height = winPtr->reqHeight + (wmPtr->height
  1134.             - wmPtr->reqGridHeight)*wmPtr->heightInc;
  1135.         }
  1136.         wmPtr->widthInc = 1;
  1137.         wmPtr->heightInc = 1;
  1138.     } else {
  1139.         if ((Tcl_GetInt(interp, argv[3], &reqWidth) != TCL_OK)
  1140.             || (Tcl_GetInt(interp, argv[4], &reqHeight) != TCL_OK)
  1141.             || (Tcl_GetInt(interp, argv[5], &widthInc) != TCL_OK)
  1142.             || (Tcl_GetInt(interp, argv[6], &heightInc) != TCL_OK)) {
  1143.         return TCL_ERROR;
  1144.         }
  1145.         if (reqWidth < 0) {
  1146.         interp->result = "baseWidth can't be < 0";
  1147.         return TCL_ERROR;
  1148.         }
  1149.         if (reqHeight < 0) {
  1150.         interp->result = "baseHeight can't be < 0";
  1151.         return TCL_ERROR;
  1152.         }
  1153.         if (widthInc < 0) {
  1154.         interp->result = "widthInc can't be < 0";
  1155.         return TCL_ERROR;
  1156.         }
  1157.         if (heightInc < 0) {
  1158.         interp->result = "heightInc can't be < 0";
  1159.         return TCL_ERROR;
  1160.         }
  1161.         Tk_SetGrid((Tk_Window) winPtr, reqWidth, reqHeight, widthInc,
  1162.             heightInc);
  1163.     }
  1164.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1165.     goto updateGeom;
  1166.     } else if ((c == 'g') && (strncmp(argv[1], "group", length) == 0)
  1167.         && (length >= 3)) {
  1168.     Tk_Window tkwin2;
  1169.     WmInfo *wmPtr2;
  1170.  
  1171.     if ((argc != 3) && (argc != 4)) {
  1172.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1173.             argv[0], " group window ?pathName?\"",
  1174.             (char *) NULL);
  1175.         return TCL_ERROR;
  1176.     }
  1177.     if (argc == 3) {
  1178.         if (wmPtr->hints.flags & WindowGroupHint) {
  1179.         interp->result = wmPtr->leaderName;
  1180.         }
  1181.         return TCL_OK;
  1182.     }
  1183.     if (*argv[3] == '\0') {
  1184.         wmPtr->hints.flags &= ~WindowGroupHint;
  1185.         if (wmPtr->leaderName != NULL) {
  1186.         ckfree(wmPtr->leaderName);
  1187.         }
  1188.         wmPtr->leaderName = NULL;
  1189.     } else {
  1190.         tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
  1191.         if (tkwin2 == NULL) {
  1192.         return TCL_ERROR;
  1193.         }
  1194.         while (!Tk_IsTopLevel(tkwin2)) {
  1195.         /*
  1196.          * Ensure that the group leader is actually a Tk toplevel.
  1197.          */
  1198.  
  1199.         tkwin2 = Tk_Parent(tkwin2);
  1200.         }
  1201.         Tk_MakeWindowExist(tkwin2);
  1202.         wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
  1203.         if (wmPtr2->wrapperPtr == NULL) {
  1204.         CreateWrapper(wmPtr2);
  1205.         }
  1206.         wmPtr->hints.window_group = Tk_WindowId(wmPtr2->wrapperPtr);
  1207.         wmPtr->hints.flags |= WindowGroupHint;
  1208.         wmPtr->leaderName = ckalloc((unsigned) (strlen(argv[3])+1));
  1209.         strcpy(wmPtr->leaderName, argv[3]);
  1210.     }
  1211.     UpdateHints(winPtr);
  1212.     } else if ((c == 'i') && (strncmp(argv[1], "iconbitmap", length) == 0)
  1213.         && (length >= 5)) {
  1214.     Pixmap pixmap;
  1215.  
  1216.     if ((argc != 3) && (argc != 4)) {
  1217.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1218.             argv[0], " iconbitmap window ?bitmap?\"",
  1219.             (char *) NULL);
  1220.         return TCL_ERROR;
  1221.     }
  1222.     if (argc == 3) {
  1223.         if (wmPtr->hints.flags & IconPixmapHint) {
  1224.         interp->result = Tk_NameOfBitmap(winPtr->display,
  1225.             wmPtr->hints.icon_pixmap);
  1226.         }
  1227.         return TCL_OK;
  1228.     }
  1229.     if (*argv[3] == '\0') {
  1230.         if (wmPtr->hints.icon_pixmap != None) {
  1231.         Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_pixmap);
  1232.         wmPtr->hints.icon_pixmap = None;
  1233.         }
  1234.         wmPtr->hints.flags &= ~IconPixmapHint;
  1235.     } else {
  1236.         pixmap = Tk_GetBitmap(interp, (Tk_Window) winPtr,
  1237.             Tk_GetUid(argv[3]));
  1238.         if (pixmap == None) {
  1239.         return TCL_ERROR;
  1240.         }
  1241.         wmPtr->hints.icon_pixmap = pixmap;
  1242.         wmPtr->hints.flags |= IconPixmapHint;
  1243.     }
  1244.     UpdateHints(winPtr);
  1245.     } else if ((c == 'i') && (strncmp(argv[1], "iconify", length) == 0)
  1246.         && (length >= 5)) {
  1247.     if (argc != 3) {
  1248.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1249.             argv[0], " iconify window\"", (char *) NULL);
  1250.         return TCL_ERROR;
  1251.     }
  1252.     if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
  1253.         Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
  1254.             "\": override-redirect flag is set", (char *) NULL);
  1255.         return TCL_ERROR;
  1256.     }
  1257.     if (wmPtr->master != None) {
  1258.         Tcl_AppendResult(interp, "can't iconify \"", winPtr->pathName,
  1259.             "\": it is a transient", (char *) NULL);
  1260.         return TCL_ERROR;
  1261.     }
  1262.     if (wmPtr->iconFor != NULL) {
  1263.         Tcl_AppendResult(interp, "can't iconify ", argv[2],
  1264.             ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
  1265.            (char *) NULL);
  1266.         return TCL_ERROR;
  1267.     }
  1268.     wmPtr->hints.initial_state = IconicState;
  1269.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  1270.         return TCL_OK;
  1271.     }
  1272.     if (wmPtr->withdrawn) {
  1273.         UpdateHints(winPtr);
  1274.         Tk_MapWindow((Tk_Window) winPtr);
  1275.         wmPtr->withdrawn = 0;
  1276.     } else {
  1277.         if (XIconifyWindow(winPtr->display, wmPtr->wrapperPtr->window,
  1278.             winPtr->screenNum) == 0) {
  1279.         interp->result =
  1280.             "couldn't send iconify message to window manager";
  1281.         return TCL_ERROR;
  1282.         }
  1283.         WaitForMapNotify(winPtr, 0);
  1284.     }
  1285.     } else if ((c == 'i') && (strncmp(argv[1], "iconmask", length) == 0)
  1286.         && (length >= 5)) {
  1287.     Pixmap pixmap;
  1288.  
  1289.     if ((argc != 3) && (argc != 4)) {
  1290.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1291.             argv[0], " iconmask window ?bitmap?\"",
  1292.             (char *) NULL);
  1293.         return TCL_ERROR;
  1294.     }
  1295.     if (argc == 3) {
  1296.         if (wmPtr->hints.flags & IconMaskHint) {
  1297.         interp->result = Tk_NameOfBitmap(winPtr->display,
  1298.             wmPtr->hints.icon_mask);
  1299.         }
  1300.         return TCL_OK;
  1301.     }
  1302.     if (*argv[3] == '\0') {
  1303.         if (wmPtr->hints.icon_mask != None) {
  1304.         Tk_FreeBitmap(winPtr->display, wmPtr->hints.icon_mask);
  1305.         }
  1306.         wmPtr->hints.flags &= ~IconMaskHint;
  1307.     } else {
  1308.         pixmap = Tk_GetBitmap(interp, tkwin, Tk_GetUid(argv[3]));
  1309.         if (pixmap == None) {
  1310.         return TCL_ERROR;
  1311.         }
  1312.         wmPtr->hints.icon_mask = pixmap;
  1313.         wmPtr->hints.flags |= IconMaskHint;
  1314.     }
  1315.     UpdateHints(winPtr);
  1316.     } else if ((c == 'i') && (strncmp(argv[1], "iconname", length) == 0)
  1317.         && (length >= 5)) {
  1318.     if (argc > 4) {
  1319.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1320.             argv[0], " iconname window ?newName?\"", (char *) NULL);
  1321.         return TCL_ERROR;
  1322.     }
  1323.     if (argc == 3) {
  1324.         interp->result = (wmPtr->iconName != NULL) ? wmPtr->iconName : "";
  1325.         return TCL_OK;
  1326.     } else {
  1327.         wmPtr->iconName = ckalloc((unsigned) (strlen(argv[3]) + 1));
  1328.         strcpy(wmPtr->iconName, argv[3]);
  1329.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1330.         XSetIconName(winPtr->display, wmPtr->wrapperPtr->window,
  1331.             wmPtr->iconName);
  1332.         }
  1333.     }
  1334.     } else if ((c == 'i') && (strncmp(argv[1], "iconposition", length) == 0)
  1335.         && (length >= 5)) {
  1336.     int x, y;
  1337.  
  1338.     if ((argc != 3) && (argc != 5)) {
  1339.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1340.             argv[0], " iconposition window ?x y?\"",
  1341.             (char *) NULL);
  1342.         return TCL_ERROR;
  1343.     }
  1344.     if (argc == 3) {
  1345.         if (wmPtr->hints.flags & IconPositionHint) {
  1346.         sprintf(interp->result, "%d %d", wmPtr->hints.icon_x,
  1347.             wmPtr->hints.icon_y);
  1348.         }
  1349.         return TCL_OK;
  1350.     }
  1351.     if (*argv[3] == '\0') {
  1352.         wmPtr->hints.flags &= ~IconPositionHint;
  1353.     } else {
  1354.         if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
  1355.             || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
  1356.         return TCL_ERROR;
  1357.         }
  1358.         wmPtr->hints.icon_x = x;
  1359.         wmPtr->hints.icon_y = y;
  1360.         wmPtr->hints.flags |= IconPositionHint;
  1361.     }
  1362.     UpdateHints(winPtr);
  1363.     } else if ((c == 'i') && (strncmp(argv[1], "iconwindow", length) == 0)
  1364.         && (length >= 5)) {
  1365.     Tk_Window tkwin2;
  1366.     WmInfo *wmPtr2;
  1367.     XSetWindowAttributes atts;
  1368.  
  1369.     if ((argc != 3) && (argc != 4)) {
  1370.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1371.             argv[0], " iconwindow window ?pathName?\"",
  1372.             (char *) NULL);
  1373.         return TCL_ERROR;
  1374.     }
  1375.     if (argc == 3) {
  1376.         if (wmPtr->icon != NULL) {
  1377.         interp->result = Tk_PathName(wmPtr->icon);
  1378.         }
  1379.         return TCL_OK;
  1380.     }
  1381.     if (*argv[3] == '\0') {
  1382.         wmPtr->hints.flags &= ~IconWindowHint;
  1383.         if (wmPtr->icon != NULL) {
  1384.         /*
  1385.          * Remove the icon window relationship.  In principle we
  1386.          * should also re-enable button events for the window, but
  1387.          * this doesn't work in general because the window manager
  1388.          * is probably selecting on them (we'll get an error if
  1389.          * we try to re-enable the events).  So, just leave the
  1390.          * icon window event-challenged;  the user will have to
  1391.          * recreate it if they want button events.
  1392.          */
  1393.  
  1394.         wmPtr2 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
  1395.         wmPtr2->iconFor = NULL;
  1396.         wmPtr2->withdrawn = 1;
  1397.         wmPtr2->hints.initial_state = WithdrawnState;
  1398.         }
  1399.         wmPtr->icon = NULL;
  1400.     } else {
  1401.         tkwin2 = Tk_NameToWindow(interp, argv[3], tkwin);
  1402.         if (tkwin2 == NULL) {
  1403.         return TCL_ERROR;
  1404.         }
  1405.         if (!Tk_IsTopLevel(tkwin2)) {
  1406.         Tcl_AppendResult(interp, "can't use ", argv[3],
  1407.             " as icon window: not at top level", (char *) NULL);
  1408.         return TCL_ERROR;
  1409.         }
  1410.         wmPtr2 = ((TkWindow *) tkwin2)->wmInfoPtr;
  1411.         if (wmPtr2->iconFor != NULL) {
  1412.         Tcl_AppendResult(interp, argv[3], " is already an icon for ",
  1413.             Tk_PathName(wmPtr2->iconFor), (char *) NULL);
  1414.         return TCL_ERROR;
  1415.         }
  1416.         if (wmPtr->icon != NULL) {
  1417.         WmInfo *wmPtr3 = ((TkWindow *) wmPtr->icon)->wmInfoPtr;
  1418.         wmPtr3->iconFor = NULL;
  1419.         wmPtr3->withdrawn = 1;
  1420.         wmPtr3->hints.initial_state = WithdrawnState;
  1421.         }
  1422.  
  1423.         /*
  1424.          * Disable button events in the icon window:  some window
  1425.          * managers (like olvwm) want to get the events themselves,
  1426.          * but X only allows one application at a time to receive
  1427.          * button events for a window.
  1428.          */
  1429.  
  1430.         atts.event_mask = Tk_Attributes(tkwin2)->event_mask
  1431.             & ~ButtonPressMask;
  1432.         Tk_ChangeWindowAttributes(tkwin2, CWEventMask, &atts);
  1433.         Tk_MakeWindowExist(tkwin2);
  1434.         if (wmPtr2->wrapperPtr == NULL) {
  1435.         CreateWrapper(wmPtr2);
  1436.         }
  1437.         wmPtr->hints.icon_window = Tk_WindowId(wmPtr2->wrapperPtr);
  1438.         wmPtr->hints.flags |= IconWindowHint;
  1439.         wmPtr->icon = tkwin2;
  1440.         wmPtr2->iconFor = (Tk_Window) winPtr;
  1441.         if (!wmPtr2->withdrawn && !(wmPtr2->flags & WM_NEVER_MAPPED)) {
  1442.         wmPtr2->withdrawn = 0;
  1443.         if (XWithdrawWindow(Tk_Display(tkwin2),
  1444.             Tk_WindowId(wmPtr2->wrapperPtr),
  1445.             Tk_ScreenNumber(tkwin2)) == 0) {
  1446.             interp->result =
  1447.                 "couldn't send withdraw message to window manager";
  1448.             return TCL_ERROR;
  1449.         }
  1450.         WaitForMapNotify((TkWindow *) tkwin2, 0);
  1451.         }
  1452.     }
  1453.     UpdateHints(winPtr);
  1454.     } else if ((c == 'm') && (strncmp(argv[1], "maxsize", length) == 0)
  1455.         && (length >= 2)) {
  1456.     int width, height;
  1457.     if ((argc != 3) && (argc != 5)) {
  1458.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1459.             argv[0], " maxsize window ?width height?\"", (char *) NULL);
  1460.         return TCL_ERROR;
  1461.     }
  1462.     if (argc == 3) {
  1463.         GetMaxSize(wmPtr, &width, &height);
  1464.         sprintf(interp->result, "%d %d", width, height);
  1465.         return TCL_OK;
  1466.     }
  1467.     if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
  1468.         || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
  1469.         return TCL_ERROR;
  1470.     }
  1471.     wmPtr->maxWidth = width;
  1472.     wmPtr->maxHeight = height;
  1473.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1474.     goto updateGeom;
  1475.     } else if ((c == 'm') && (strncmp(argv[1], "minsize", length) == 0)
  1476.         && (length >= 2)) {
  1477.     int width, height;
  1478.     if ((argc != 3) && (argc != 5)) {
  1479.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1480.             argv[0], " minsize window ?width height?\"", (char *) NULL);
  1481.         return TCL_ERROR;
  1482.     }
  1483.     if (argc == 3) {
  1484.         sprintf(interp->result, "%d %d", wmPtr->minWidth,
  1485.             wmPtr->minHeight);
  1486.         return TCL_OK;
  1487.     }
  1488.     if ((Tcl_GetInt(interp, argv[3], &width) != TCL_OK)
  1489.         || (Tcl_GetInt(interp, argv[4], &height) != TCL_OK)) {
  1490.         return TCL_ERROR;
  1491.     }
  1492.     wmPtr->minWidth = width;
  1493.     wmPtr->minHeight = height;
  1494.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1495.     goto updateGeom;
  1496.     } else if ((c == 'o')
  1497.         && (strncmp(argv[1], "overrideredirect", length) == 0)) {
  1498.     int boolean;
  1499.     XSetWindowAttributes atts;
  1500.  
  1501.     if ((argc != 3) && (argc != 4)) {
  1502.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1503.             argv[0], " overrideredirect window ?boolean?\"",
  1504.             (char *) NULL);
  1505.         return TCL_ERROR;
  1506.     }
  1507.     if (argc == 3) {
  1508.         if (Tk_Attributes((Tk_Window) winPtr)->override_redirect) {
  1509.         interp->result = "1";
  1510.         } else {
  1511.         interp->result = "0";
  1512.         }
  1513.         return TCL_OK;
  1514.     }
  1515.     if (Tcl_GetBoolean(interp, argv[3], &boolean) != TCL_OK) {
  1516.         return TCL_ERROR;
  1517.     }
  1518.     atts.override_redirect = (boolean) ? True : False;
  1519.     Tk_ChangeWindowAttributes((Tk_Window) winPtr, CWOverrideRedirect,
  1520.         &atts);
  1521.     if (winPtr->wmInfoPtr->wrapperPtr != NULL) {
  1522.         Tk_ChangeWindowAttributes(
  1523.             (Tk_Window) winPtr->wmInfoPtr->wrapperPtr,
  1524.             CWOverrideRedirect, &atts);
  1525.     }
  1526.     } else if ((c == 'p') && (strncmp(argv[1], "positionfrom", length) == 0)
  1527.         && (length >= 2)) {
  1528.     if ((argc != 3) && (argc != 4)) {
  1529.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1530.             argv[0], " positionfrom window ?user/program?\"",
  1531.             (char *) NULL);
  1532.         return TCL_ERROR;
  1533.     }
  1534.     if (argc == 3) {
  1535.         if (wmPtr->sizeHintsFlags & USPosition) {
  1536.         interp->result = "user";
  1537.         } else if (wmPtr->sizeHintsFlags & PPosition) {
  1538.         interp->result = "program";
  1539.         }
  1540.         return TCL_OK;
  1541.     }
  1542.     if (*argv[3] == '\0') {
  1543.         wmPtr->sizeHintsFlags &= ~(USPosition|PPosition);
  1544.     } else {
  1545.         c = argv[3][0];
  1546.         length = strlen(argv[3]);
  1547.         if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
  1548.         wmPtr->sizeHintsFlags &= ~PPosition;
  1549.         wmPtr->sizeHintsFlags |= USPosition;
  1550.         } else if ((c == 'p') && (strncmp(argv[3], "program", length) == 0)) {
  1551.         wmPtr->sizeHintsFlags &= ~USPosition;
  1552.         wmPtr->sizeHintsFlags |= PPosition;
  1553.         } else {
  1554.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  1555.             "\": must be program or user", (char *) NULL);
  1556.         return TCL_ERROR;
  1557.         }
  1558.     }
  1559.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1560.     goto updateGeom;
  1561.     } else if ((c == 'p') && (strncmp(argv[1], "protocol", length) == 0)
  1562.         && (length >= 2)) {
  1563.     register ProtocolHandler *protPtr, *prevPtr;
  1564.     Atom protocol;
  1565.     int cmdLength;
  1566.  
  1567.     if ((argc < 3) || (argc > 5)) {
  1568.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1569.             argv[0], " protocol window ?name? ?command?\"",
  1570.             (char *) NULL);
  1571.         return TCL_ERROR;
  1572.     }
  1573.     if (argc == 3) {
  1574.         /*
  1575.          * Return a list of all defined protocols for the window.
  1576.          */
  1577.         for (protPtr = wmPtr->protPtr; protPtr != NULL;
  1578.             protPtr = protPtr->nextPtr) {
  1579.         Tcl_AppendElement(interp,
  1580.             Tk_GetAtomName((Tk_Window) winPtr, protPtr->protocol));
  1581.         }
  1582.         return TCL_OK;
  1583.     }
  1584.     protocol = Tk_InternAtom((Tk_Window) winPtr, argv[3]);
  1585.     if (argc == 4) {
  1586.         /*
  1587.          * Return the command to handle a given protocol.
  1588.          */
  1589.         for (protPtr = wmPtr->protPtr; protPtr != NULL;
  1590.             protPtr = protPtr->nextPtr) {
  1591.         if (protPtr->protocol == protocol) {
  1592.             interp->result = protPtr->command;
  1593.             return TCL_OK;
  1594.         }
  1595.         }
  1596.         return TCL_OK;
  1597.     }
  1598.  
  1599.     /*
  1600.      * Delete any current protocol handler, then create a new
  1601.      * one with the specified command, unless the command is
  1602.      * empty.
  1603.      */
  1604.  
  1605.     for (protPtr = wmPtr->protPtr, prevPtr = NULL; protPtr != NULL;
  1606.         prevPtr = protPtr, protPtr = protPtr->nextPtr) {
  1607.         if (protPtr->protocol == protocol) {
  1608.         if (prevPtr == NULL) {
  1609.             wmPtr->protPtr = protPtr->nextPtr;
  1610.         } else {
  1611.             prevPtr->nextPtr = protPtr->nextPtr;
  1612.         }
  1613.         Tcl_EventuallyFree((ClientData) protPtr, TCL_DYNAMIC);
  1614.         break;
  1615.         }
  1616.     }
  1617.     cmdLength = strlen(argv[4]);
  1618.     if (cmdLength > 0) {
  1619.         protPtr = (ProtocolHandler *) ckalloc(HANDLER_SIZE(cmdLength));
  1620.         protPtr->protocol = protocol;
  1621.         protPtr->nextPtr = wmPtr->protPtr;
  1622.         wmPtr->protPtr = protPtr;
  1623.         protPtr->interp = interp;
  1624.         strcpy(protPtr->command, argv[4]);
  1625.     }
  1626.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1627.         UpdateWmProtocols(wmPtr);
  1628.     }
  1629.     } else if ((c == 'r') && (strncmp(argv[1], "resizable", length) == 0)) {
  1630.     int width, height;
  1631.  
  1632.     if ((argc != 3) && (argc != 5)) {
  1633.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1634.             argv[0], " resizable window ?width height?\"",
  1635.             (char *) NULL);
  1636.         return TCL_ERROR;
  1637.     }
  1638.     if (argc == 3) {
  1639.         sprintf(interp->result, "%d %d",
  1640.             (wmPtr->flags  & WM_WIDTH_NOT_RESIZABLE) ? 0 : 1,
  1641.             (wmPtr->flags  & WM_HEIGHT_NOT_RESIZABLE) ? 0 : 1);
  1642.         return TCL_OK;
  1643.     }
  1644.     if ((Tcl_GetBoolean(interp, argv[3], &width) != TCL_OK)
  1645.         || (Tcl_GetBoolean(interp, argv[4], &height) != TCL_OK)) {
  1646.         return TCL_ERROR;
  1647.     }
  1648.     if (width) {
  1649.         wmPtr->flags &= ~WM_WIDTH_NOT_RESIZABLE;
  1650.     } else {
  1651.         wmPtr->flags |= WM_WIDTH_NOT_RESIZABLE;
  1652.     }
  1653.     if (height) {
  1654.         wmPtr->flags &= ~WM_HEIGHT_NOT_RESIZABLE;
  1655.     } else {
  1656.         wmPtr->flags |= WM_HEIGHT_NOT_RESIZABLE;
  1657.     }
  1658.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1659.     goto updateGeom;
  1660.     } else if ((c == 's') && (strncmp(argv[1], "sizefrom", length) == 0)
  1661.         && (length >= 2)) {
  1662.     if ((argc != 3) && (argc != 4)) {
  1663.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1664.             argv[0], " sizefrom window ?user|program?\"",
  1665.             (char *) NULL);
  1666.         return TCL_ERROR;
  1667.     }
  1668.     if (argc == 3) {
  1669.         if (wmPtr->sizeHintsFlags & USSize) {
  1670.         interp->result = "user";
  1671.         } else if (wmPtr->sizeHintsFlags & PSize) {
  1672.         interp->result = "program";
  1673.         }
  1674.         return TCL_OK;
  1675.     }
  1676.     if (*argv[3] == '\0') {
  1677.         wmPtr->sizeHintsFlags &= ~(USSize|PSize);
  1678.     } else {
  1679.         c = argv[3][0];
  1680.         length = strlen(argv[3]);
  1681.         if ((c == 'u') && (strncmp(argv[3], "user", length) == 0)) {
  1682.         wmPtr->sizeHintsFlags &= ~PSize;
  1683.         wmPtr->sizeHintsFlags |= USSize;
  1684.         } else if ((c == 'p')
  1685.             && (strncmp(argv[3], "program", length) == 0)) {
  1686.         wmPtr->sizeHintsFlags &= ~USSize;
  1687.         wmPtr->sizeHintsFlags |= PSize;
  1688.         } else {
  1689.         Tcl_AppendResult(interp, "bad argument \"", argv[3],
  1690.             "\": must be program or user", (char *) NULL);
  1691.         return TCL_ERROR;
  1692.         }
  1693.     }
  1694.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1695.     goto updateGeom;
  1696.     } else if ((c == 's') && (strncmp(argv[1], "state", length) == 0)
  1697.         && (length >= 2)) {
  1698.     if (argc != 3) {
  1699.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1700.             argv[0], " state window\"", (char *) NULL);
  1701.         return TCL_ERROR;
  1702.     }
  1703.     if (wmPtr->iconFor != NULL) {
  1704.         interp->result = "icon";
  1705.     } else if (wmPtr->withdrawn) {
  1706.         interp->result = "withdrawn";
  1707.     } else if (Tk_IsMapped((Tk_Window) winPtr)
  1708.         || ((wmPtr->flags & WM_NEVER_MAPPED)
  1709.         && (wmPtr->hints.initial_state == NormalState))) {
  1710.         interp->result = "normal";
  1711.     } else {
  1712.         interp->result = "iconic";
  1713.     }
  1714.     } else if ((c == 't') && (strncmp(argv[1], "title", length) == 0)
  1715.         && (length >= 2)) {
  1716.     if (argc > 4) {
  1717.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1718.             argv[0], " title window ?newTitle?\"", (char *) NULL);
  1719.         return TCL_ERROR;
  1720.     }
  1721.     if (argc == 3) {
  1722.         interp->result = (wmPtr->title != NULL) ? wmPtr->title
  1723.             : winPtr->nameUid;
  1724.         return TCL_OK;
  1725.     } else {
  1726.         wmPtr->title = ckalloc((unsigned) (strlen(argv[3]) + 1));
  1727.         strcpy(wmPtr->title, argv[3]);
  1728.         if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1729.         XTextProperty textProp;
  1730.  
  1731.         if (XStringListToTextProperty(&wmPtr->title, 1,
  1732.             &textProp)  != 0) {
  1733.             XSetWMName(winPtr->display, wmPtr->wrapperPtr->window,
  1734.                 &textProp);
  1735.             XFree((char *) textProp.value);
  1736.         }
  1737.         }
  1738.     }
  1739.     } else if ((c == 't') && (strncmp(argv[1], "transient", length) == 0)
  1740.         && (length >= 3)) {
  1741.     Tk_Window master;
  1742.     WmInfo *wmPtr2;
  1743.  
  1744.     if ((argc != 3) && (argc != 4)) {
  1745.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1746.             argv[0], " transient window ?master?\"", (char *) NULL);
  1747.         return TCL_ERROR;
  1748.     }
  1749.     if (argc == 3) {
  1750.         if (wmPtr->master != None) {
  1751.         interp->result = wmPtr->masterWindowName;
  1752.         }
  1753.         return TCL_OK;
  1754.     }
  1755.     if (argv[3][0] == '\0') {
  1756.         wmPtr->master = None;
  1757.         if (wmPtr->masterWindowName != NULL) {
  1758.         ckfree(wmPtr->masterWindowName);
  1759.         }
  1760.         wmPtr->masterWindowName = NULL;
  1761.     } else {
  1762.         master = Tk_NameToWindow(interp, argv[3], tkwin);
  1763.         if (master == NULL) {
  1764.         return TCL_ERROR;
  1765.         }
  1766.         while (!Tk_IsTopLevel(master)) {
  1767.         /*
  1768.          * Ensure that the master window is actually a Tk toplevel.
  1769.          */
  1770.  
  1771.         master = Tk_Parent(master);
  1772.         }
  1773.         Tk_MakeWindowExist(master);
  1774.         wmPtr2 = ((TkWindow *) master)->wmInfoPtr;
  1775.         if (wmPtr2->wrapperPtr == NULL) {
  1776.         CreateWrapper(wmPtr2);
  1777.         }
  1778.         wmPtr->master = Tk_WindowId(wmPtr2->wrapperPtr);
  1779.         wmPtr->masterWindowName = ckalloc((unsigned) (strlen(argv[3])+1));
  1780.         strcpy(wmPtr->masterWindowName, argv[3]);
  1781.     }
  1782.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  1783.         XSetTransientForHint(winPtr->display, wmPtr->wrapperPtr->window,
  1784.             wmPtr->master);
  1785.     }
  1786.     } else if ((c == 'w') && (strncmp(argv[1], "withdraw", length) == 0)
  1787.         && (length >= 2)) {
  1788.     if (argc != 3) {
  1789.         Tcl_AppendResult(interp, "wrong # arguments: must be \"",
  1790.             argv[0], " withdraw window\"", (char *) NULL);
  1791.         return TCL_ERROR;
  1792.     }
  1793.     if (wmPtr->iconFor != NULL) {
  1794.         Tcl_AppendResult(interp, "can't withdraw ", argv[2],
  1795.             ": it is an icon for ", Tk_PathName(wmPtr->iconFor),
  1796.             (char *) NULL);
  1797.         return TCL_ERROR;
  1798.     }
  1799.     wmPtr->hints.initial_state = WithdrawnState;
  1800.     wmPtr->withdrawn = 1;
  1801.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  1802.         return TCL_OK;
  1803.     }
  1804.     if (XWithdrawWindow(winPtr->display, wmPtr->wrapperPtr->window,
  1805.         winPtr->screenNum) == 0) {
  1806.         interp->result =
  1807.             "couldn't send withdraw message to window manager";
  1808.         return TCL_ERROR;
  1809.     }
  1810.     WaitForMapNotify(winPtr, 0);
  1811.     } else {
  1812.     Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1],
  1813.         "\": must be aspect, client, command, deiconify, ",
  1814.         "focusmodel, frame, geometry, grid, group, iconbitmap, ",
  1815.         "iconify, iconmask, iconname, iconposition, ",
  1816.         "iconwindow, maxsize, minsize, overrideredirect, ",
  1817.         "positionfrom, protocol, resizable, sizefrom, state, title, ",
  1818.         "transient, or withdraw",
  1819.         (char *) NULL);
  1820.     return TCL_ERROR;
  1821.     }
  1822.     return TCL_OK;
  1823.  
  1824.     updateGeom:
  1825.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1826.     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1827.     wmPtr->flags |= WM_UPDATE_PENDING;
  1828.     }
  1829.     return TCL_OK;
  1830. }
  1831.  
  1832. /*
  1833.  *----------------------------------------------------------------------
  1834.  *
  1835.  * Tk_SetGrid --
  1836.  *
  1837.  *    This procedure is invoked by a widget when it wishes to set a grid
  1838.  *    coordinate system that controls the size of a top-level window.
  1839.  *    It provides a C interface equivalent to the "wm grid" command and
  1840.  *    is usually asscoiated with the -setgrid option.
  1841.  *
  1842.  * Results:
  1843.  *    None.
  1844.  *
  1845.  * Side effects:
  1846.  *    Grid-related information will be passed to the window manager, so
  1847.  *    that the top-level window associated with tkwin will resize on
  1848.  *    even grid units.  If some other window already controls gridding
  1849.  *    for the top-level window then this procedure call has no effect.
  1850.  *
  1851.  *----------------------------------------------------------------------
  1852.  */
  1853.  
  1854. void
  1855. Tk_SetGrid(tkwin, reqWidth, reqHeight, widthInc, heightInc)
  1856.     Tk_Window tkwin;        /* Token for window.  New window mgr info
  1857.                  * will be posted for the top-level window
  1858.                  * associated with this window. */
  1859.     int reqWidth;        /* Width (in grid units) corresponding to
  1860.                  * the requested geometry for tkwin. */
  1861.     int reqHeight;        /* Height (in grid units) corresponding to
  1862.                  * the requested geometry for tkwin. */
  1863.     int widthInc, heightInc;    /* Pixel increments corresponding to a
  1864.                  * change of one grid unit. */
  1865. {
  1866.     TkWindow *winPtr = (TkWindow *) tkwin;
  1867.     register WmInfo *wmPtr;
  1868.  
  1869.     /*
  1870.      * Find the top-level window for tkwin, plus the window manager
  1871.      * information.
  1872.      */
  1873.  
  1874.     while (!(winPtr->flags & TK_TOP_LEVEL)) {
  1875.     winPtr = winPtr->parentPtr;
  1876.     if (winPtr == NULL) {
  1877.         /*
  1878.          * The window is being deleted... just skip this operation.
  1879.          */
  1880.  
  1881.         return;
  1882.     }
  1883.     }
  1884.     wmPtr = winPtr->wmInfoPtr;
  1885.  
  1886.     if ((wmPtr->gridWin != NULL) && (wmPtr->gridWin != tkwin)) {
  1887.     return;
  1888.     }
  1889.  
  1890.     if ((wmPtr->reqGridWidth == reqWidth)
  1891.         && (wmPtr->reqGridHeight == reqHeight)
  1892.         && (wmPtr->widthInc == widthInc)
  1893.         && (wmPtr->heightInc == heightInc)
  1894.         && ((wmPtr->sizeHintsFlags & (PBaseSize|PResizeInc))
  1895.             == (PBaseSize|PResizeInc) )) {
  1896.     return;
  1897.     }
  1898.  
  1899.     /*
  1900.      * If gridding was previously off, then forget about any window
  1901.      * size requests made by the user or via "wm geometry":  these are
  1902.      * in pixel units and there's no easy way to translate them to
  1903.      * grid units since the new requested size of the top-level window in
  1904.      * pixels may not yet have been registered yet (it may filter up
  1905.      * the hierarchy in DoWhenIdle handlers).  However, if the window
  1906.      * has never been mapped yet then just leave the window size alone:
  1907.      * assume that it is intended to be in grid units but just happened
  1908.      * to have been specified before this procedure was called.
  1909.      */
  1910.  
  1911.     if ((wmPtr->gridWin == NULL) && !(wmPtr->flags & WM_NEVER_MAPPED)) {
  1912.     wmPtr->width = -1;
  1913.     wmPtr->height = -1;
  1914.     }
  1915.  
  1916.     /* 
  1917.      * Set the new gridding information, and start the process of passing
  1918.      * all of this information to the window manager.
  1919.      */
  1920.  
  1921.     wmPtr->gridWin = tkwin;
  1922.     wmPtr->reqGridWidth = reqWidth;
  1923.     wmPtr->reqGridHeight = reqHeight;
  1924.     wmPtr->widthInc = widthInc;
  1925.     wmPtr->heightInc = heightInc;
  1926.     wmPtr->sizeHintsFlags |= PBaseSize|PResizeInc;
  1927.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1928.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1929.     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1930.     wmPtr->flags |= WM_UPDATE_PENDING;
  1931.     }
  1932. }
  1933.  
  1934. /*
  1935.  *----------------------------------------------------------------------
  1936.  *
  1937.  * Tk_UnsetGrid --
  1938.  *
  1939.  *    This procedure cancels the effect of a previous call
  1940.  *    to Tk_SetGrid.
  1941.  *
  1942.  * Results:
  1943.  *    None.
  1944.  *
  1945.  * Side effects:
  1946.  *    If tkwin currently controls gridding for its top-level window,
  1947.  *    gridding is cancelled for that top-level window;  if some other
  1948.  *    window controls gridding then this procedure has no effect.
  1949.  *
  1950.  *----------------------------------------------------------------------
  1951.  */
  1952.  
  1953. void
  1954. Tk_UnsetGrid(tkwin)
  1955.     Tk_Window tkwin;        /* Token for window that is currently
  1956.                  * controlling gridding. */
  1957. {
  1958.     TkWindow *winPtr = (TkWindow *) tkwin;
  1959.     register WmInfo *wmPtr;
  1960.  
  1961.     /*
  1962.      * Find the top-level window for tkwin, plus the window manager
  1963.      * information.
  1964.      */
  1965.  
  1966.     while (!(winPtr->flags & TK_TOP_LEVEL)) {
  1967.     winPtr = winPtr->parentPtr;
  1968.     if (winPtr == NULL) {
  1969.         /*
  1970.          * The window is being deleted... just skip this operation.
  1971.          */
  1972.  
  1973.         return;
  1974.     }
  1975.     }
  1976.     wmPtr = winPtr->wmInfoPtr;
  1977.     if (tkwin != wmPtr->gridWin) {
  1978.     return;
  1979.     }
  1980.  
  1981.     wmPtr->gridWin = NULL;
  1982.     wmPtr->sizeHintsFlags &= ~(PBaseSize|PResizeInc);
  1983.     if (wmPtr->width != -1) {
  1984.     wmPtr->width = winPtr->reqWidth + (wmPtr->width
  1985.         - wmPtr->reqGridWidth)*wmPtr->widthInc;
  1986.     wmPtr->height = winPtr->reqHeight + (wmPtr->height
  1987.         - wmPtr->reqGridHeight)*wmPtr->heightInc;
  1988.     }
  1989.     wmPtr->widthInc = 1;
  1990.     wmPtr->heightInc = 1;
  1991.  
  1992.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  1993.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  1994.     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  1995.     wmPtr->flags |= WM_UPDATE_PENDING;
  1996.     }
  1997. }
  1998.  
  1999. /*
  2000.  *----------------------------------------------------------------------
  2001.  *
  2002.  * ConfigureEvent --
  2003.  *
  2004.  *    This procedure is called to handle ConfigureNotify events on
  2005.  *    wrapper windows.
  2006.  *
  2007.  * Results:
  2008.  *    None.
  2009.  *
  2010.  * Side effects:
  2011.  *    Information gets updated in the WmInfo structure for the window
  2012.  *    and the toplevel itself gets repositioned within the wrapper.
  2013.  *
  2014.  *----------------------------------------------------------------------
  2015.  */
  2016.  
  2017. static void
  2018. ConfigureEvent(wmPtr, configEventPtr)
  2019.     WmInfo *wmPtr;            /* Information about toplevel window. */
  2020.     XConfigureEvent *configEventPtr;    /* Event that just occurred for
  2021.                      * wmPtr->wrapperPtr. */
  2022. {
  2023.     TkWindow *wrapperPtr = wmPtr->wrapperPtr;
  2024.     TkWindow *winPtr = wmPtr->winPtr;
  2025.  
  2026.     /* 
  2027.      * Update size information from the event.  There are a couple of
  2028.      * tricky points here:
  2029.      *
  2030.      * 1. If the user changed the size externally then set wmPtr->width
  2031.      *    and wmPtr->height just as if a "wm geometry" command had been
  2032.      *    invoked with the same information.
  2033.      * 2. However, if the size is changing in response to a request
  2034.      *    coming from us (WM_SYNC_PENDING is set), then don't set wmPtr->width
  2035.      *    or wmPtr->height if they were previously -1 (otherwise the
  2036.      *    window will stop tracking geometry manager requests).
  2037.      */
  2038.  
  2039.     if (((wrapperPtr->changes.width != configEventPtr->width)
  2040.         || (wrapperPtr->changes.height != configEventPtr->height))
  2041.         && !(wmPtr->flags & WM_SYNC_PENDING)){
  2042.     if (wmTracing) {
  2043.         printf("TopLevelEventProc: user changed %s size to %dx%d\n",
  2044.             winPtr->pathName, configEventPtr->width,
  2045.             configEventPtr->height);
  2046.     }
  2047.     if ((wmPtr->width == -1)
  2048.         && (configEventPtr->width == winPtr->reqWidth)) {
  2049.         /*
  2050.          * Don't set external width, since the user didn't change it
  2051.          * from what the widgets asked for.
  2052.          */
  2053.     } else {
  2054.         /*
  2055.          * Note: if this window is embedded then don't set the external
  2056.          * size, since it came from the containing application, not the
  2057.          * user.  In this case we want to keep sending our size requests
  2058.          * to the containing application;  if the user fixes the size
  2059.          * of that application then it will still percolate down to us
  2060.          * in the right way.
  2061.          */
  2062.  
  2063.         if (!(winPtr->flags & TK_EMBEDDED)) {
  2064.         if (wmPtr->gridWin != NULL) {
  2065.             wmPtr->width = wmPtr->reqGridWidth
  2066.                 + (configEventPtr->width
  2067.                 - winPtr->reqWidth)/wmPtr->widthInc;
  2068.             if (wmPtr->width < 0) {
  2069.             wmPtr->width = 0;
  2070.             }
  2071.         } else {
  2072.             wmPtr->width = configEventPtr->width;
  2073.         }
  2074.         }
  2075.     }
  2076.     if ((wmPtr->height == -1)
  2077.         && (configEventPtr->height ==
  2078.             (winPtr->reqHeight + wmPtr->menuHeight))) {
  2079.         /*
  2080.          * Don't set external height, since the user didn't change it
  2081.          * from what the widgets asked for.
  2082.          */
  2083.     } else {
  2084.         /*
  2085.          * See note for wmPtr->width about not setting external size
  2086.          * for embedded windows.
  2087.          */
  2088.  
  2089.         if (!(winPtr->flags & TK_EMBEDDED)) {
  2090.         if (wmPtr->gridWin != NULL) {
  2091.             wmPtr->height = wmPtr->reqGridHeight
  2092.                 + (configEventPtr->height - wmPtr->menuHeight
  2093.                 - winPtr->reqHeight)/wmPtr->heightInc;
  2094.             if (wmPtr->height < 0) {
  2095.             wmPtr->height = 0;
  2096.             }
  2097.         } else {
  2098.             wmPtr->height = configEventPtr->height - wmPtr->menuHeight;
  2099.         }
  2100.             }
  2101.     }
  2102.     wmPtr->configWidth = configEventPtr->width;
  2103.     wmPtr->configHeight = configEventPtr->height;
  2104.     }
  2105.  
  2106.     if (wmTracing) {
  2107.     printf("ConfigureEvent: %s x = %d y = %d, width = %d, height = %d",
  2108.         winPtr->pathName, configEventPtr->x, configEventPtr->y,
  2109.         configEventPtr->width, configEventPtr->height);
  2110.     printf(" send_event = %d, serial = %ld\n", configEventPtr->send_event,
  2111.         configEventPtr->serial);
  2112.     }
  2113.     wrapperPtr->changes.width = configEventPtr->width;
  2114.     wrapperPtr->changes.height = configEventPtr->height;
  2115.     wrapperPtr->changes.border_width = configEventPtr->border_width;
  2116.     wrapperPtr->changes.sibling = configEventPtr->above;
  2117.     wrapperPtr->changes.stack_mode = Above;
  2118.  
  2119.     /*
  2120.      * Reparenting window managers make life difficult.  If the
  2121.      * window manager reparents a top-level window then the x and y
  2122.      * information that comes in events for the window is wrong:
  2123.      * it gives the location of the window inside its decorative
  2124.      * parent, rather than the location of the window in root
  2125.      * coordinates, which is what we want.  Window managers
  2126.      * are supposed to send synthetic events with the correct
  2127.      * information, but ICCCM doesn't require them to do this
  2128.      * under all conditions, and the information provided doesn't
  2129.      * include everything we need here.  So, the code below
  2130.      * maintains a bunch of information about the parent window.
  2131.      * If the window hasn't been reparented, we pretend that
  2132.      * there is a parent shrink-wrapped around the window.
  2133.      */
  2134.  
  2135.     if ((wmPtr->reparent == None) || !ComputeReparentGeometry(wmPtr)) {
  2136.     wmPtr->parentWidth = configEventPtr->width
  2137.         + 2*configEventPtr->border_width;
  2138.     wmPtr->parentHeight = configEventPtr->height
  2139.         + 2*configEventPtr->border_width;
  2140.     wrapperPtr->changes.x = wmPtr->x = configEventPtr->x;
  2141.     wrapperPtr->changes.y = wmPtr->y = configEventPtr->y;
  2142.     if (wmPtr->flags & WM_NEGATIVE_X) {
  2143.         wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
  2144.     }
  2145.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  2146.         wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
  2147.     }
  2148.     }
  2149.  
  2150.     /*
  2151.      * Make sure that the toplevel and menubar are properly positioned within
  2152.      * the wrapper.
  2153.      */
  2154.  
  2155.     XMoveResizeWindow(winPtr->display, winPtr->window, 0,
  2156.         wmPtr->menuHeight, (unsigned) wrapperPtr->changes.width,
  2157.         (unsigned) (wrapperPtr->changes.height - wmPtr->menuHeight));
  2158.     if ((wmPtr->menubar != NULL)
  2159.         && ((Tk_Width(wmPtr->menubar) != wrapperPtr->changes.width)
  2160.         || (Tk_Height(wmPtr->menubar) != wmPtr->menuHeight))) {
  2161.     Tk_MoveResizeWindow(wmPtr->menubar, 0, 0, wrapperPtr->changes.width,
  2162.         wmPtr->menuHeight);
  2163.     }
  2164.  
  2165.     /*
  2166.      * Update the coordinates in the toplevel (they should refer to the
  2167.      * position in root window coordinates, not the coordinates of the
  2168.      * wrapper window).  Then synthesize a ConfigureNotify event to tell
  2169.      * the application about the change.
  2170.      */
  2171.  
  2172.     winPtr->changes.x = wrapperPtr->changes.x;
  2173.     winPtr->changes.y = wrapperPtr->changes.y + wmPtr->menuHeight;
  2174.     winPtr->changes.width = wrapperPtr->changes.width;
  2175.     winPtr->changes.height = wrapperPtr->changes.height - wmPtr->menuHeight;
  2176.     TkDoConfigureNotify(winPtr);
  2177. }
  2178.  
  2179. /*
  2180.  *----------------------------------------------------------------------
  2181.  *
  2182.  * ReparentEvent --
  2183.  *
  2184.  *    This procedure is called to handle ReparentNotify events on
  2185.  *    wrapper windows.
  2186.  *
  2187.  * Results:
  2188.  *    None.
  2189.  *
  2190.  * Side effects:
  2191.  *    Information gets updated in the WmInfo structure for the window.
  2192.  *
  2193.  *----------------------------------------------------------------------
  2194.  */
  2195.  
  2196. static void
  2197. ReparentEvent(wmPtr, reparentEventPtr)
  2198.     WmInfo *wmPtr;            /* Information about toplevel window. */
  2199.     XReparentEvent *reparentEventPtr;    /* Event that just occurred for
  2200.                      * wmPtr->wrapperPtr. */
  2201. {
  2202.     TkWindow *wrapperPtr = wmPtr->wrapperPtr;
  2203.     Window vRoot, ancestor, *children, dummy2, *virtualRootPtr;
  2204.     Atom actualType;
  2205.     int actualFormat;
  2206.     unsigned long numItems, bytesAfter;
  2207.     unsigned int dummy;
  2208.     Tk_ErrorHandler handler;
  2209.  
  2210.     /*
  2211.      * Identify the root window for wrapperPtr.  This is tricky because of
  2212.      * virtual root window managers like tvtwm.  If the window has a
  2213.      * property named __SWM_ROOT or __WM_ROOT then this property gives
  2214.      * the id for a virtual root window that should be used instead of
  2215.      * the root window of the screen.
  2216.      */
  2217.  
  2218.     vRoot = RootWindow(wrapperPtr->display, wrapperPtr->screenNum);
  2219.     wmPtr->vRoot = None;
  2220.     handler = Tk_CreateErrorHandler(wrapperPtr->display, -1, -1, -1,
  2221.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  2222.     if (((XGetWindowProperty(wrapperPtr->display, wrapperPtr->window,
  2223.         Tk_InternAtom((Tk_Window) wrapperPtr, "__WM_ROOT"), 0, (long) 1,
  2224.         False, XA_WINDOW, &actualType, &actualFormat, &numItems,
  2225.         &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
  2226.         && (actualType == XA_WINDOW))
  2227.         || ((XGetWindowProperty(wrapperPtr->display, wrapperPtr->window,
  2228.         Tk_InternAtom((Tk_Window) wrapperPtr, "__SWM_ROOT"), 0, (long) 1,
  2229.         False, XA_WINDOW, &actualType, &actualFormat, &numItems,
  2230.         &bytesAfter, (unsigned char **) &virtualRootPtr) == Success)
  2231.         && (actualType == XA_WINDOW))) {
  2232.     if ((actualFormat == 32) && (numItems == 1)) {
  2233.         vRoot = wmPtr->vRoot = *virtualRootPtr;
  2234.     } else if (wmTracing) {
  2235.         printf("%s format %d numItems %ld\n",
  2236.             "ReparentEvent got bogus VROOT property:", actualFormat,
  2237.             numItems);
  2238.     }
  2239.     XFree((char *) virtualRootPtr);
  2240.     }
  2241.     Tk_DeleteErrorHandler(handler);
  2242.  
  2243.     if (wmTracing) {
  2244.     printf("ReparentEvent: %s reparented to 0x%x, vRoot = 0x%x\n",
  2245.         wmPtr->winPtr->pathName,
  2246.         (unsigned int) reparentEventPtr->parent, (unsigned int) vRoot);
  2247.     }
  2248.  
  2249.     /*
  2250.      * Fetch correct geometry information for the new virtual root.
  2251.      */
  2252.  
  2253.     UpdateVRootGeometry(wmPtr);
  2254.  
  2255.     /*
  2256.      * If the window's new parent is the root window, then mark it as
  2257.      * no longer reparented.
  2258.      */
  2259.  
  2260.     if (reparentEventPtr->parent == vRoot) {
  2261.     noReparent:
  2262.     wmPtr->reparent = None;
  2263.     wmPtr->parentWidth = wrapperPtr->changes.width;
  2264.     wmPtr->parentHeight = wrapperPtr->changes.height;
  2265.     wmPtr->xInParent = wmPtr->yInParent = 0;
  2266.     wrapperPtr->changes.x = reparentEventPtr->x;
  2267.     wrapperPtr->changes.y = reparentEventPtr->y;
  2268.     return;
  2269.     }
  2270.  
  2271.     /*
  2272.      * Search up the window hierarchy to find the ancestor of this
  2273.      * window that is just below the (virtual) root.  This is tricky
  2274.      * because it's possible that things have changed since the event
  2275.      * was generated so that the ancestry indicated by the event no
  2276.      * longer exists.  If this happens then an error will occur and
  2277.      * we just discard the event (there will be a more up-to-date
  2278.      * ReparentNotify event coming later).
  2279.      */
  2280.  
  2281.     handler = Tk_CreateErrorHandler(wrapperPtr->display, -1, -1, -1,
  2282.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  2283.     wmPtr->reparent = reparentEventPtr->parent;
  2284.     while (1) {
  2285.     if (XQueryTree(wrapperPtr->display, wmPtr->reparent, &dummy2, &ancestor,
  2286.         &children, &dummy) == 0) {
  2287.         Tk_DeleteErrorHandler(handler);
  2288.         goto noReparent;
  2289.     }
  2290.     XFree((char *) children);
  2291.     if ((ancestor == vRoot) ||
  2292.         (ancestor == RootWindow(wrapperPtr->display,
  2293.         wrapperPtr->screenNum))) {
  2294.         break;
  2295.     }
  2296.     wmPtr->reparent = ancestor;
  2297.     }
  2298.     Tk_DeleteErrorHandler(handler);
  2299.  
  2300.     if (!ComputeReparentGeometry(wmPtr)) {
  2301.     goto noReparent;
  2302.     }
  2303. }
  2304.  
  2305. /*
  2306.  *----------------------------------------------------------------------
  2307.  *
  2308.  * ComputeReparentGeometry --
  2309.  *
  2310.  *    This procedure is invoked to recompute geometry information
  2311.  *    related to a reparented top-level window, such as the position
  2312.  *    and total size of the parent and the position within it of
  2313.  *    the top-level window.
  2314.  *
  2315.  * Results:
  2316.  *    The return value is 1 if everything completed successfully
  2317.  *    and 0 if an error occurred while querying information about
  2318.  *    winPtr's parents.  In this case winPtr is marked as no longer
  2319.  *    being reparented.
  2320.  *
  2321.  * Side effects:
  2322.  *    Geometry information in wmPtr, wmPtr->winPtr, and
  2323.  *    wmPtr->wrapperPtr gets updated.
  2324.  *
  2325.  *----------------------------------------------------------------------
  2326.  */
  2327.  
  2328. static int
  2329. ComputeReparentGeometry(wmPtr)
  2330.     WmInfo *wmPtr;        /* Information about toplevel window
  2331.                  * whose reparent info is to be recomputed. */
  2332. {
  2333.     TkWindow *wrapperPtr = wmPtr->wrapperPtr;
  2334.     int width, height, bd;
  2335.     unsigned int dummy;
  2336.     int xOffset, yOffset, x, y;
  2337.     Window dummy2;
  2338.     Status status;
  2339.     Tk_ErrorHandler handler;
  2340.  
  2341.     handler = Tk_CreateErrorHandler(wrapperPtr->display, -1, -1, -1,
  2342.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  2343.     (void) XTranslateCoordinates(wrapperPtr->display, wrapperPtr->window,
  2344.         wmPtr->reparent, 0, 0, &xOffset, &yOffset, &dummy2);
  2345.     status = XGetGeometry(wrapperPtr->display, wmPtr->reparent,
  2346.         &dummy2, &x, &y, (unsigned int *) &width,
  2347.         (unsigned int *) &height, (unsigned int *) &bd, &dummy);
  2348.     Tk_DeleteErrorHandler(handler);
  2349.     if (status == 0) {
  2350.     /*
  2351.      * It appears that the reparented parent went away and
  2352.      * no-one told us.  Reset the window to indicate that
  2353.      * it's not reparented.
  2354.      */
  2355.     wmPtr->reparent = None;
  2356.     wmPtr->xInParent = wmPtr->yInParent = 0;
  2357.     return 0;
  2358.     }
  2359.     wmPtr->xInParent = xOffset + bd;
  2360.     wmPtr->yInParent = yOffset + bd;
  2361.     wmPtr->parentWidth = width + 2*bd;
  2362.     wmPtr->parentHeight = height + 2*bd;
  2363.  
  2364.     /*
  2365.      * Some tricky issues in updating wmPtr->x and wmPtr->y:
  2366.      *
  2367.      * 1. Don't update them if the event occurred because of something
  2368.      * we did (i.e. WM_SYNC_PENDING and WM_MOVE_PENDING are both set).
  2369.      * This is because window managers treat coords differently than Tk,
  2370.      * and no two window managers are alike. If the window manager moved
  2371.      * the window because we told it to, remember the coordinates we told
  2372.      * it, not the ones it actually moved it to.  This allows us to move
  2373.      * the window back to the same coordinates later and get the same
  2374.      * result. Without this check, windows can "walk" across the screen
  2375.      * under some conditions.
  2376.      *
  2377.      * 2. Don't update wmPtr->x and wmPtr->y unless wrapperPtr->changes.x
  2378.      * or wrapperPtr->changes.y has changed (otherwise a size change can
  2379.      * spoof us into thinking that the position changed too and defeat
  2380.      * the intent of (1) above.
  2381.      *
  2382.      * (As of 9/96 the above 2 comments appear to be stale.  They're
  2383.      * being left in place as a reminder of what was once true (and
  2384.      * perhaps should still be true?)).
  2385.      *
  2386.      * 3. Ignore size changes coming from the window system if we're
  2387.      * about to change the size ourselves but haven't seen the event for
  2388.      * it yet:  our size change is supposed to take priority.
  2389.      */
  2390.  
  2391.     if (!(wmPtr->flags & WM_MOVE_PENDING)
  2392.         && ((wmPtr->wrapperPtr->changes.x != (x + wmPtr->xInParent))
  2393.         || (wmPtr->wrapperPtr->changes.y != (y + wmPtr->yInParent)))) {
  2394.     wmPtr->x = x;
  2395.     if (wmPtr->flags & WM_NEGATIVE_X) {
  2396.         wmPtr->x = wmPtr->vRootWidth - (wmPtr->x + wmPtr->parentWidth);
  2397.     }
  2398.     wmPtr->y = y;
  2399.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  2400.         wmPtr->y = wmPtr->vRootHeight - (wmPtr->y + wmPtr->parentHeight);
  2401.     }
  2402.     }
  2403.  
  2404.     wmPtr->wrapperPtr->changes.x = x + wmPtr->xInParent;
  2405.     wmPtr->wrapperPtr->changes.y = y + wmPtr->yInParent;
  2406.     if (wmTracing) {
  2407.     printf("wrapperPtr coords %d,%d, wmPtr coords %d,%d, offsets %d %d\n",
  2408.         wrapperPtr->changes.x, wrapperPtr->changes.y,
  2409.         wmPtr->x, wmPtr->y, wmPtr->xInParent, wmPtr->yInParent);
  2410.     }
  2411.     return 1;
  2412. }
  2413.  
  2414. /*
  2415.  *----------------------------------------------------------------------
  2416.  *
  2417.  * WrapperEventProc --
  2418.  *
  2419.  *    This procedure is invoked by the event loop when a wrapper window
  2420.  *    is restructured.
  2421.  *
  2422.  * Results:
  2423.  *    None.
  2424.  *
  2425.  * Side effects:
  2426.  *    Tk's internal data structures for the window get modified to
  2427.  *    reflect the structural change.
  2428.  *
  2429.  *----------------------------------------------------------------------
  2430.  */
  2431.  
  2432. static void
  2433. WrapperEventProc(clientData, eventPtr)
  2434.     ClientData clientData;        /* Information about toplevel window. */
  2435.     XEvent *eventPtr;            /* Event that just happened. */
  2436. {
  2437.     WmInfo *wmPtr = (WmInfo *) clientData;
  2438.     XEvent mapEvent;
  2439.  
  2440.     wmPtr->flags |= WM_VROOT_OFFSET_STALE;
  2441.     if (eventPtr->type == DestroyNotify) {
  2442.     Tk_ErrorHandler handler;
  2443.  
  2444.     if (!(wmPtr->wrapperPtr->flags & TK_ALREADY_DEAD)) {
  2445.         /*
  2446.          * A top-level window was deleted externally (e.g., by the window
  2447.          * manager).  This is probably not a good thing, but cleanup as
  2448.          * best we can.  The error handler is needed because
  2449.          * Tk_DestroyWindow will try to destroy the window, but of course
  2450.          * it's already gone.
  2451.          */
  2452.     
  2453.         handler = Tk_CreateErrorHandler(wmPtr->winPtr->display, -1, -1, -1,
  2454.             (Tk_ErrorProc *) NULL, (ClientData) NULL);
  2455.         Tk_DestroyWindow((Tk_Window) wmPtr->winPtr);
  2456.         Tk_DeleteErrorHandler(handler);
  2457.     }
  2458.     if (wmTracing) {
  2459.         printf("TopLevelEventProc: %s deleted\n", wmPtr->winPtr->pathName);
  2460.     }
  2461.     } else if (eventPtr->type == ConfigureNotify) {
  2462.     /*
  2463.      * Ignore the event if the window has never been mapped yet.
  2464.      * Such an event occurs only in weird cases like changing the
  2465.      * internal border width of a top-level window, which results
  2466.      * in a synthetic Configure event.  These events are not relevant
  2467.      * to us, and if we process them confusion may result (e.g. we
  2468.      * may conclude erroneously that the user repositioned or resized
  2469.      * the window).
  2470.      */
  2471.  
  2472.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  2473.         ConfigureEvent(wmPtr, &eventPtr->xconfigure);
  2474.     }
  2475.     } else if (eventPtr->type == MapNotify) {
  2476.     wmPtr->wrapperPtr->flags |= TK_MAPPED;
  2477.     wmPtr->winPtr->flags |= TK_MAPPED;
  2478.     XMapWindow(wmPtr->winPtr->display, wmPtr->winPtr->window);
  2479.     goto doMapEvent;
  2480.     } else if (eventPtr->type == UnmapNotify) {
  2481.     wmPtr->wrapperPtr->flags &= ~TK_MAPPED;
  2482.     wmPtr->winPtr->flags &= ~TK_MAPPED;
  2483.     XUnmapWindow(wmPtr->winPtr->display, wmPtr->winPtr->window);
  2484.     goto doMapEvent;
  2485.     } else if (eventPtr->type == ReparentNotify) {
  2486.     ReparentEvent(wmPtr, &eventPtr->xreparent);
  2487.     }
  2488.     return;
  2489.  
  2490.     doMapEvent:
  2491.     mapEvent = *eventPtr;
  2492.     mapEvent.xmap.event = wmPtr->winPtr->window;
  2493.     mapEvent.xmap.window = wmPtr->winPtr->window;
  2494.     Tk_HandleEvent(&mapEvent);
  2495. }
  2496.  
  2497. /*
  2498.  *----------------------------------------------------------------------
  2499.  *
  2500.  * TopLevelReqProc --
  2501.  *
  2502.  *    This procedure is invoked by the geometry manager whenever
  2503.  *    the requested size for a top-level window is changed.
  2504.  *
  2505.  * Results:
  2506.  *    None.
  2507.  *
  2508.  * Side effects:
  2509.  *    Arrange for the window to be resized to satisfy the request
  2510.  *    (this happens as a when-idle action).
  2511.  *
  2512.  *----------------------------------------------------------------------
  2513.  */
  2514.  
  2515.     /* ARGSUSED */
  2516. static void
  2517. TopLevelReqProc(dummy, tkwin)
  2518.     ClientData dummy;            /* Not used. */
  2519.     Tk_Window tkwin;            /* Information about window. */
  2520. {
  2521.     TkWindow *winPtr = (TkWindow *) tkwin;
  2522.     WmInfo *wmPtr;
  2523.  
  2524.     wmPtr = winPtr->wmInfoPtr;
  2525.  
  2526.     if ((wmPtr->width >= 0) && (wmPtr->height >= 0)) {
  2527.     /*
  2528.      * Explicit dimensions have been set for this window, so we
  2529.      * should ignore the geometry request.  It's actually important
  2530.      * to ignore the geometry request because, due to quirks in
  2531.      * window managers, invoking UpdateGeometryInfo may cause the
  2532.      * window to move.  For example, if "wm geometry -10-20" was
  2533.      * invoked, the window may be positioned incorrectly the first
  2534.      * time it appears (because we didn't know the proper width of
  2535.      * the window manager borders); if we invoke UpdateGeometryInfo
  2536.      * again, the window will be positioned correctly, which may
  2537.      * cause it to jump on the screen.
  2538.      */
  2539.  
  2540.     return;
  2541.     }
  2542.  
  2543.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  2544.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  2545.     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  2546.     wmPtr->flags |= WM_UPDATE_PENDING;
  2547.     }
  2548.  
  2549.     /*
  2550.      * If the window isn't being positioned by its upper left corner
  2551.      * then we have to move it as well.
  2552.      */
  2553.  
  2554.     if (wmPtr->flags & (WM_NEGATIVE_X | WM_NEGATIVE_Y)) {
  2555.     wmPtr->flags |= WM_MOVE_PENDING;
  2556.     }
  2557. }
  2558.  
  2559. /*
  2560.  *----------------------------------------------------------------------
  2561.  *
  2562.  * UpdateGeometryInfo --
  2563.  *
  2564.  *    This procedure is invoked when a top-level window is first
  2565.  *    mapped, and also as a when-idle procedure, to bring the
  2566.  *    geometry and/or position of a top-level window back into
  2567.  *    line with what has been requested by the user and/or widgets.
  2568.  *    This procedure doesn't return until the window manager has
  2569.  *    responded to the geometry change.
  2570.  *
  2571.  * Results:
  2572.  *    None.
  2573.  *
  2574.  * Side effects:
  2575.  *    The size and location of both the toplevel window and its wrapper
  2576.  *    may change, unless the WM prevents that from happening.
  2577.  *
  2578.  *----------------------------------------------------------------------
  2579.  */
  2580.  
  2581. static void
  2582. UpdateGeometryInfo(clientData)
  2583.     ClientData clientData;        /* Pointer to the window's record. */
  2584. {
  2585.     register TkWindow *winPtr = (TkWindow *) clientData;
  2586.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2587.     int x, y, width, height;
  2588.     unsigned long serial;
  2589.  
  2590.     wmPtr->flags &= ~WM_UPDATE_PENDING;
  2591.  
  2592.     /*
  2593.      * Compute the new size for the top-level window.  See the
  2594.      * user documentation for details on this, but the size
  2595.      * requested depends on (a) the size requested internally
  2596.      * by the window's widgets, (b) the size requested by the
  2597.      * user in a "wm geometry" command or via wm-based interactive
  2598.      * resizing (if any), and (c) whether or not the window is
  2599.      * gridded.  Don't permit sizes <= 0 because this upsets
  2600.      * the X server.
  2601.      */
  2602.  
  2603.     if (wmPtr->width == -1) {
  2604.     width = winPtr->reqWidth;
  2605.     } else if (wmPtr->gridWin != NULL) {
  2606.     width = winPtr->reqWidth
  2607.         + (wmPtr->width - wmPtr->reqGridWidth)*wmPtr->widthInc;
  2608.     } else {
  2609.     width = wmPtr->width;
  2610.     }
  2611.     if (width <= 0) {
  2612.     width = 1;
  2613.     }
  2614.     if (wmPtr->height == -1) {
  2615.     height = winPtr->reqHeight;
  2616.     } else if (wmPtr->gridWin != NULL) {
  2617.     height = winPtr->reqHeight
  2618.         + (wmPtr->height - wmPtr->reqGridHeight)*wmPtr->heightInc;
  2619.     } else {
  2620.     height = wmPtr->height;
  2621.     }
  2622.     if (height <= 0) {
  2623.     height = 1;
  2624.     }
  2625.  
  2626.     /*
  2627.      * Compute the new position for the upper-left pixel of the window's
  2628.      * decorative frame.  This is tricky, because we need to include the
  2629.      * border widths supplied by a reparented parent in this calculation,
  2630.      * but can't use the parent's current overall size since that may
  2631.      * change as a result of this code.
  2632.      */
  2633.  
  2634.     if (wmPtr->flags & WM_NEGATIVE_X) {
  2635.     x = wmPtr->vRootWidth - wmPtr->x
  2636.         - (width + (wmPtr->parentWidth - winPtr->changes.width));
  2637.     } else {
  2638.     x =  wmPtr->x;
  2639.     }
  2640.     if (wmPtr->flags & WM_NEGATIVE_Y) {
  2641.     y = wmPtr->vRootHeight - wmPtr->y
  2642.         - (height + (wmPtr->parentHeight - winPtr->changes.height));
  2643.     } else {
  2644.     y =  wmPtr->y;
  2645.     }
  2646.  
  2647.     /*
  2648.      * If the window's size is going to change and the window is
  2649.      * supposed to not be resizable by the user, then we have to
  2650.      * update the size hints.  There may also be a size-hint-update
  2651.      * request pending from somewhere else, too.
  2652.      */
  2653.  
  2654.     if (((width != winPtr->changes.width)
  2655.         || (height != winPtr->changes.height))
  2656.         && (wmPtr->gridWin == NULL)
  2657.         && ((wmPtr->sizeHintsFlags & (PMinSize|PMaxSize)) == 0)) {
  2658.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  2659.     }
  2660.     if (wmPtr->flags & WM_UPDATE_SIZE_HINTS) {
  2661.     UpdateSizeHints(winPtr);
  2662.     }
  2663.  
  2664.     /*
  2665.      * Reconfigure the wrapper if it isn't already configured correctly.
  2666.      * A few tricky points:
  2667.      *
  2668.      * 1. If the window is embeddedand the container is also in this
  2669.      *    process, don't actually reconfigure the window; just pass the
  2670.      *    desired size on to the container.  Also, zero out any position
  2671.      *    information, since embedded windows are not allowed to move.
  2672.      * 2. Sometimes the window manager will give us a different size
  2673.      *    than we asked for (e.g. mwm has a minimum size for windows), so
  2674.      *    base the size check on what we *asked for* last time, not what we
  2675.      *    got.
  2676.      * 3. Can't just reconfigure always, because we may not get a
  2677.      *    ConfigureNotify event back if nothing changed, so
  2678.      *    WaitForConfigureNotify will hang a long time.
  2679.      * 4. Don't move window unless a new position has been requested for
  2680.      *      it.  This is because of "features" in some window managers (e.g.
  2681.      *    twm, as of 4/24/91) where they don't interpret coordinates
  2682.      *    according to ICCCM.  Moving a window to its current location may
  2683.      *    cause it to shift position on the screen.
  2684.      */
  2685.  
  2686.     if ((winPtr->flags & (TK_EMBEDDED|TK_BOTH_HALVES))
  2687.         == (TK_EMBEDDED|TK_BOTH_HALVES)) {
  2688.     /*
  2689.      * This window is embedded and the container is also in this
  2690.      * process, so we don't need to do anything special about the
  2691.      * geometry, except to make sure that the desired size is known
  2692.      * by the container.  Also, zero out any position information,
  2693.      * since embedded windows are not allowed to move.
  2694.      */
  2695.  
  2696.     wmPtr->x = wmPtr->y = 0;
  2697.     wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
  2698.     height += wmPtr->menuHeight;
  2699.     Tk_GeometryRequest((Tk_Window) TkpGetOtherWindow(winPtr),
  2700.         width, height);
  2701.     return;
  2702.     }
  2703.     serial = NextRequest(winPtr->display);
  2704.     height += wmPtr->menuHeight;
  2705.     if (wmPtr->flags & WM_MOVE_PENDING) {
  2706.     if ((x == winPtr->changes.x) && (y == winPtr->changes.y)
  2707.         && (width == wmPtr->wrapperPtr->changes.width)
  2708.         && (height == wmPtr->wrapperPtr->changes.height)) {
  2709.         /*
  2710.          * The window already has the correct geometry, so don't bother
  2711.          * to configure it;  the X server appears to ignore these
  2712.          * requests, so we won't get back a ConfigureNotify and the
  2713.          * WaitForConfigureNotify call below will hang for a while.
  2714.          */
  2715.  
  2716.         wmPtr->flags &= ~WM_MOVE_PENDING;
  2717.         return;
  2718.     }
  2719.     wmPtr->configWidth = width;
  2720.     wmPtr->configHeight = height;
  2721.     if (wmTracing) {
  2722.        printf("UpdateGeometryInfo moving to %d %d, resizing to %d x %d,\n",
  2723.                    x, y, width, height);
  2724.     }
  2725.     XMoveResizeWindow(winPtr->display, wmPtr->wrapperPtr->window, x, y,
  2726.         (unsigned) width, (unsigned) height);
  2727.     } else if ((width != wmPtr->configWidth)
  2728.         || (height != wmPtr->configHeight)) {
  2729.     if ((width == wmPtr->wrapperPtr->changes.width)
  2730.         && (height == wmPtr->wrapperPtr->changes.height)) {
  2731.         /*
  2732.          * The window is already just the size we want, so don't bother
  2733.          * to configure it;  the X server appears to ignore these
  2734.          * requests, so we won't get back a ConfigureNotify and the
  2735.          * WaitForConfigureNotify call below will hang for a while.
  2736.          */
  2737.  
  2738.         return;
  2739.     }
  2740.     wmPtr->configWidth = width;
  2741.     wmPtr->configHeight = height;
  2742.     if (wmTracing) {
  2743.         printf("UpdateGeometryInfo resizing to %d x %d\n", width, height);
  2744.     }
  2745.     XResizeWindow(winPtr->display, wmPtr->wrapperPtr->window,
  2746.         (unsigned) width, (unsigned) height);
  2747.     } else if ((wmPtr->menubar != NULL)
  2748.         && ((Tk_Width(wmPtr->menubar) != wmPtr->wrapperPtr->changes.width)
  2749.         || (Tk_Height(wmPtr->menubar) != wmPtr->menuHeight))) {
  2750.     /*
  2751.      * It is possible that the window's overall size has not changed
  2752.      * but the menu size has.
  2753.      */
  2754.     
  2755.     Tk_MoveResizeWindow(wmPtr->menubar, 0, 0,
  2756.         wmPtr->wrapperPtr->changes.width, wmPtr->menuHeight);
  2757.     XResizeWindow(winPtr->display, wmPtr->wrapperPtr->window,
  2758.         (unsigned) width, (unsigned) height);
  2759.     } else {
  2760.     return;
  2761.     }
  2762.  
  2763.     /*
  2764.      * Wait for the configure operation to complete.  Don't need to do
  2765.      * this, however, if the window is about to be mapped:  it will be
  2766.      * taken care of elsewhere.
  2767.      */
  2768.  
  2769.     if (!(wmPtr->flags & WM_ABOUT_TO_MAP)) {
  2770.     WaitForConfigureNotify(winPtr, serial);
  2771.     }
  2772. }
  2773.  
  2774. /*
  2775.  *--------------------------------------------------------------
  2776.  *
  2777.  * UpdateSizeHints --
  2778.  *
  2779.  *    This procedure is called to update the window manager's
  2780.  *    size hints information from the information in a WmInfo
  2781.  *    structure.
  2782.  *
  2783.  * Results:
  2784.  *    None.
  2785.  *
  2786.  * Side effects:
  2787.  *    Properties get changed for winPtr.
  2788.  *
  2789.  *--------------------------------------------------------------
  2790.  */
  2791.  
  2792. static void
  2793. UpdateSizeHints(winPtr)
  2794.     TkWindow *winPtr;
  2795. {
  2796.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  2797.     XSizeHints *hintsPtr;
  2798.     int maxWidth, maxHeight;
  2799.  
  2800.     wmPtr->flags &= ~WM_UPDATE_SIZE_HINTS;
  2801.  
  2802.     hintsPtr = XAllocSizeHints();
  2803.     if (hintsPtr == NULL) {
  2804.     return;
  2805.     }
  2806.  
  2807.     /*
  2808.      * Compute the pixel-based sizes for the various fields in the
  2809.      * size hints structure, based on the grid-based sizes in
  2810.      * our structure.
  2811.      */
  2812.  
  2813.     GetMaxSize(wmPtr, &maxWidth, &maxHeight);
  2814.     if (wmPtr->gridWin != NULL) {
  2815.     hintsPtr->base_width = winPtr->reqWidth
  2816.         - (wmPtr->reqGridWidth * wmPtr->widthInc);
  2817.     if (hintsPtr->base_width < 0) {
  2818.         hintsPtr->base_width = 0;
  2819.     }
  2820.     hintsPtr->base_height = winPtr->reqHeight + wmPtr->menuHeight
  2821.         - (wmPtr->reqGridHeight * wmPtr->heightInc);
  2822.     if (hintsPtr->base_height < 0) {
  2823.         hintsPtr->base_height = 0;
  2824.     }
  2825.     hintsPtr->min_width = hintsPtr->base_width
  2826.         + (wmPtr->minWidth * wmPtr->widthInc);
  2827.     hintsPtr->min_height = hintsPtr->base_height
  2828.         + (wmPtr->minHeight * wmPtr->heightInc);
  2829.     hintsPtr->max_width = hintsPtr->base_width
  2830.         + (maxWidth * wmPtr->widthInc);
  2831.     hintsPtr->max_height = hintsPtr->base_height
  2832.         + (maxHeight * wmPtr->heightInc);
  2833.     } else {
  2834.     hintsPtr->min_width = wmPtr->minWidth;
  2835.     hintsPtr->min_height = wmPtr->minHeight;
  2836.     hintsPtr->max_width = maxWidth;
  2837.     hintsPtr->max_height = maxHeight;
  2838.     hintsPtr->base_width = 0;
  2839.     hintsPtr->base_height = 0;
  2840.     }
  2841.     hintsPtr->width_inc = wmPtr->widthInc;
  2842.     hintsPtr->height_inc = wmPtr->heightInc;
  2843.     hintsPtr->min_aspect.x = wmPtr->minAspect.x;
  2844.     hintsPtr->min_aspect.y = wmPtr->minAspect.y;
  2845.     hintsPtr->max_aspect.x = wmPtr->maxAspect.x;
  2846.     hintsPtr->max_aspect.y = wmPtr->maxAspect.y;
  2847.     hintsPtr->win_gravity = wmPtr->gravity;
  2848.     hintsPtr->flags = wmPtr->sizeHintsFlags | PMinSize | PMaxSize;
  2849.  
  2850.     /*
  2851.      * If the window isn't supposed to be resizable, then set the
  2852.      * minimum and maximum dimensions to be the same.
  2853.      */
  2854.  
  2855.     if (wmPtr->flags & WM_WIDTH_NOT_RESIZABLE) {
  2856.     if (wmPtr->width >= 0) {
  2857.         hintsPtr->min_width = wmPtr->width;
  2858.     } else {
  2859.         hintsPtr->min_width = winPtr->reqWidth;
  2860.     }
  2861.     hintsPtr->max_width = hintsPtr->min_width;
  2862.     }
  2863.     if (wmPtr->flags & WM_HEIGHT_NOT_RESIZABLE) {
  2864.     if (wmPtr->height >= 0) {
  2865.         hintsPtr->min_height = wmPtr->height;
  2866.     } else {
  2867.         hintsPtr->min_height = winPtr->reqHeight + wmPtr->menuHeight;
  2868.     }
  2869.     hintsPtr->max_height = hintsPtr->min_height;
  2870.     }
  2871.  
  2872.     XSetWMNormalHints(winPtr->display, wmPtr->wrapperPtr->window, hintsPtr);
  2873.  
  2874.     XFree((char *) hintsPtr);
  2875. }
  2876.  
  2877. /*
  2878.  *----------------------------------------------------------------------
  2879.  *
  2880.  * WaitForConfigureNotify --
  2881.  *
  2882.  *    This procedure is invoked in order to synchronize with the
  2883.  *    window manager.  It waits for a ConfigureNotify event to
  2884.  *    arrive, signalling that the window manager has seen an attempt
  2885.  *    on our part to move or resize a top-level window.
  2886.  *
  2887.  * Results:
  2888.  *    None.
  2889.  *
  2890.  * Side effects:
  2891.  *    Delays the execution of the process until a ConfigureNotify event
  2892.  *    arrives with serial number at least as great as serial.  This
  2893.  *    is useful for two reasons:
  2894.  *
  2895.  *    1. It's important to distinguish ConfigureNotify events that are
  2896.  *       coming in response to a request we've made from those generated
  2897.  *       spontaneously by the user.  The reason for this is that if the
  2898.  *       user resizes the window we take that as an order to ignore
  2899.  *       geometry requests coming from inside the window hierarchy.  If
  2900.  *       we accidentally interpret a response to our request as a
  2901.  *       user-initiated action, the window will stop responding to
  2902.  *       new geometry requests.  To make this distinction, (a) this
  2903.  *       procedure sets a flag for TopLevelEventProc to indicate that
  2904.  *       we're waiting to sync with the wm, and (b) all changes to
  2905.  *       the size of a top-level window are followed by calls to this
  2906.  *       procedure.
  2907.  *    2. Races and confusion can come about if there are multiple
  2908.  *       operations outstanding at a time (e.g. two different resizes
  2909.  *       of the top-level window:  it's hard to tell which of the
  2910.  *       ConfigureNotify events coming back is for which request).
  2911.  *    While waiting, all events covered by StructureNotifyMask are
  2912.  *    processed and all others are deferred.
  2913.  *
  2914.  *----------------------------------------------------------------------
  2915.  */
  2916.  
  2917. static void
  2918. WaitForConfigureNotify(winPtr, serial)
  2919.     TkWindow *winPtr;        /* Top-level window for which we want
  2920.                  * to see a ConfigureNotify. */
  2921.     unsigned long serial;    /* Serial number of resize request.  Want to
  2922.                  * be sure wm has seen this. */
  2923. {
  2924.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  2925.     XEvent event;
  2926.     int diff, code;
  2927.     int gotConfig = 0;
  2928.  
  2929.     /*
  2930.      * One more tricky detail about this procedure.  In some cases the
  2931.      * window manager will decide to ignore a configure request (e.g.
  2932.      * because it thinks the window is already in the right place).
  2933.      * To avoid hanging in this situation, only wait for a few seconds,
  2934.      * then give up.
  2935.      */
  2936.  
  2937.     while (!gotConfig) {
  2938.     wmPtr->flags |= WM_SYNC_PENDING;
  2939.     code = WaitForEvent(winPtr->display, wmPtr->wrapperPtr->window,
  2940.         ConfigureNotify, &event);
  2941.     wmPtr->flags &= ~WM_SYNC_PENDING;
  2942.     if (code != TCL_OK) {
  2943.         if (wmTracing) {
  2944.         printf("WaitForConfigureNotify giving up on %s\n",
  2945.             winPtr->pathName);
  2946.         }
  2947.         break;
  2948.     }
  2949.     diff = event.xconfigure.serial - serial;
  2950.     if (diff >= 0) {
  2951.         gotConfig = 1;
  2952.     }
  2953.     }
  2954.     wmPtr->flags &= ~WM_MOVE_PENDING;
  2955.     if (wmTracing) {
  2956.     printf("WaitForConfigureNotify finished with %s, serial %ld\n",
  2957.         winPtr->pathName, serial);
  2958.     }
  2959. }
  2960.  
  2961. /*
  2962.  *----------------------------------------------------------------------
  2963.  *
  2964.  * WaitForEvent --
  2965.  *
  2966.  *    This procedure is used by WaitForConfigureNotify and
  2967.  *    WaitForMapNotify to wait for an event of a certain type
  2968.  *    to arrive.
  2969.  *
  2970.  * Results:
  2971.  *    Under normal conditions, TCL_OK is returned and an event for
  2972.  *    display and window that matches "mask" is stored in *eventPtr.
  2973.  *    This event  has already been processed by Tk before this procedure
  2974.  *    returns.  If a long time goes by with no event of the right type
  2975.  *    arriving, or if an error occurs while waiting for the event to
  2976.  *    arrive, then TCL_ERROR is returned.
  2977.  *
  2978.  * Side effects:
  2979.  *    While waiting for the desired event to occur, Configurenotify
  2980.  *    events for window are processed, as are all ReparentNotify events,
  2981.  *
  2982.  *----------------------------------------------------------------------
  2983.  */
  2984.  
  2985. static int
  2986. WaitForEvent(display, window, type, eventPtr)
  2987.     Display *display;        /* Display event is coming from. */
  2988.     Window window;        /* Window for which event is desired. */
  2989.     int type;            /* Type of event that is wanted. */
  2990.     XEvent *eventPtr;        /* Place to store event. */
  2991. {
  2992.     WaitRestrictInfo info;
  2993.     Tk_RestrictProc *oldRestrictProc;
  2994.     ClientData oldRestrictData;
  2995.     Tcl_Time timeout;
  2996.  
  2997.     /*
  2998.      * Set up an event filter to select just the events we want, and
  2999.      * a timer handler, then wait for events until we get the event
  3000.      * we want or a timeout happens.
  3001.      */
  3002.  
  3003.     info.display = display;
  3004.     info.window = window;
  3005.     info.type = type;
  3006.     info.eventPtr = eventPtr;
  3007.     info.foundEvent = 0;
  3008.     oldRestrictProc = Tk_RestrictEvents(WaitRestrictProc, (ClientData) &info,
  3009.         &oldRestrictData);
  3010.  
  3011.     TclpGetTime(&timeout);
  3012.     timeout.sec += 2;
  3013.  
  3014.     while (!info.foundEvent) {
  3015.     if (!TkUnixDoOneXEvent(&timeout)) {
  3016.         break;
  3017.     }
  3018.     }
  3019.     (void) Tk_RestrictEvents(oldRestrictProc, oldRestrictData,
  3020.         &oldRestrictData);
  3021.     if (info.foundEvent) {
  3022.     return TCL_OK;
  3023.     }
  3024.     return TCL_ERROR;
  3025. }
  3026.  
  3027. /*
  3028.  *----------------------------------------------------------------------
  3029.  *
  3030.  * WaitRestrictProc --
  3031.  *
  3032.  *    This procedure is a Tk_RestrictProc that is used to filter
  3033.  *    events while WaitForEvent is active.
  3034.  *
  3035.  * Results:
  3036.  *    Returns TK_PROCESS_EVENT if the right event is found.  Also
  3037.  *    returns TK_PROCESS_EVENT if any ReparentNotify event is found
  3038.  *    for window or if the event is a ConfigureNotify for window.
  3039.  *    Otherwise returns TK_DEFER_EVENT.
  3040.  *
  3041.  * Side effects:
  3042.  *    An event may get stored in the area indicated by the caller
  3043.  *    of WaitForEvent.
  3044.  *
  3045.  *----------------------------------------------------------------------
  3046.  */
  3047.  
  3048. static Tk_RestrictAction
  3049. WaitRestrictProc(clientData, eventPtr)
  3050.     ClientData clientData;    /* Pointer to WaitRestrictInfo structure. */
  3051.     XEvent *eventPtr;        /* Event that is about to be handled. */
  3052. {
  3053.     WaitRestrictInfo *infoPtr = (WaitRestrictInfo *) clientData;
  3054.  
  3055.     if (eventPtr->type == ReparentNotify) {
  3056.     return TK_PROCESS_EVENT;
  3057.     }
  3058.     if ((eventPtr->xany.window != infoPtr->window)
  3059.         || (eventPtr->xany.display != infoPtr->display)) {
  3060.     return TK_DEFER_EVENT;
  3061.     }
  3062.     if (eventPtr->type == infoPtr->type) {
  3063.     *infoPtr->eventPtr = *eventPtr;
  3064.     infoPtr->foundEvent = 1;
  3065.     return TK_PROCESS_EVENT;
  3066.     }
  3067.     if (eventPtr->type == ConfigureNotify) {
  3068.     return TK_PROCESS_EVENT;
  3069.     }
  3070.     return TK_DEFER_EVENT;
  3071. }
  3072.  
  3073. /*
  3074.  *----------------------------------------------------------------------
  3075.  *
  3076.  * WaitForMapNotify --
  3077.  *
  3078.  *    This procedure is invoked in order to synchronize with the
  3079.  *    window manager.  It waits for the window's mapped state to
  3080.  *    reach the value given by mapped.
  3081.  *
  3082.  * Results:
  3083.  *    None.
  3084.  *
  3085.  * Side effects:
  3086.  *    Delays the execution of the process until winPtr becomes mapped
  3087.  *    or unmapped, depending on the "mapped" argument.  This allows us
  3088.  *    to synchronize with the window manager, and allows us to
  3089.  *    identify changes in window size that come about when the window
  3090.  *    manager first starts managing the window (as opposed to those
  3091.  *    requested interactively by the user later).  See the comments
  3092.  *    for WaitForConfigureNotify and WM_SYNC_PENDING.  While waiting,
  3093.  *    all events covered by StructureNotifyMask are processed and all
  3094.  *    others are deferred.
  3095.  *
  3096.  *----------------------------------------------------------------------
  3097.  */
  3098.  
  3099. static void
  3100. WaitForMapNotify(winPtr, mapped)
  3101.     TkWindow *winPtr;        /* Top-level window for which we want
  3102.                  * to see a particular mapping state. */
  3103.     int mapped;            /* If non-zero, wait for window to become
  3104.                  * mapped, otherwise wait for it to become
  3105.                  * unmapped. */
  3106. {
  3107.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  3108.     XEvent event;
  3109.     int code;
  3110.  
  3111.     while (1) {
  3112.     if (mapped) {
  3113.         if (winPtr->flags & TK_MAPPED) {
  3114.         break;
  3115.         }
  3116.     } else if (!(winPtr->flags & TK_MAPPED)) {
  3117.         break;
  3118.     }
  3119.     wmPtr->flags |= WM_SYNC_PENDING;
  3120.     code = WaitForEvent(winPtr->display, wmPtr->wrapperPtr->window,
  3121.         mapped ? MapNotify : UnmapNotify, &event);
  3122.     wmPtr->flags &= ~WM_SYNC_PENDING;
  3123.     if (code != TCL_OK) {
  3124.         /*
  3125.          * There are some bizarre situations in which the window
  3126.          * manager can't respond or chooses not to (e.g. if we've
  3127.          * got a grab set it can't respond).  If this happens then
  3128.          * just quit.
  3129.          */
  3130.  
  3131.         if (wmTracing) {
  3132.         printf("WaitForMapNotify giving up on %s\n", winPtr->pathName);
  3133.         }
  3134.         break;
  3135.     }
  3136.     }
  3137.     wmPtr->flags &= ~WM_MOVE_PENDING;
  3138.     if (wmTracing) {
  3139.     printf("WaitForMapNotify finished with %s\n", winPtr->pathName);
  3140.     }
  3141. }
  3142.  
  3143. /*
  3144.  *--------------------------------------------------------------
  3145.  *
  3146.  * UpdateHints --
  3147.  *
  3148.  *    This procedure is called to update the window manager's
  3149.  *    hints information from the information in a WmInfo
  3150.  *    structure.
  3151.  *
  3152.  * Results:
  3153.  *    None.
  3154.  *
  3155.  * Side effects:
  3156.  *    Properties get changed for winPtr.
  3157.  *
  3158.  *--------------------------------------------------------------
  3159.  */
  3160.  
  3161. static void
  3162. UpdateHints(winPtr)
  3163.     TkWindow *winPtr;
  3164. {
  3165.     WmInfo *wmPtr = winPtr->wmInfoPtr;
  3166.  
  3167.     if (wmPtr->flags & WM_NEVER_MAPPED) {
  3168.     return;
  3169.     }
  3170.     XSetWMHints(winPtr->display, wmPtr->wrapperPtr->window, &wmPtr->hints);
  3171. }
  3172.  
  3173. /*
  3174.  *--------------------------------------------------------------
  3175.  *
  3176.  * ParseGeometry --
  3177.  *
  3178.  *    This procedure parses a geometry string and updates
  3179.  *    information used to control the geometry of a top-level
  3180.  *    window.
  3181.  *
  3182.  * Results:
  3183.  *    A standard Tcl return value, plus an error message in
  3184.  *    interp->result if an error occurs.
  3185.  *
  3186.  * Side effects:
  3187.  *    The size and/or location of winPtr may change.
  3188.  *
  3189.  *--------------------------------------------------------------
  3190.  */
  3191.  
  3192. static int
  3193. ParseGeometry(interp, string, winPtr)
  3194.     Tcl_Interp *interp;        /* Used for error reporting. */
  3195.     char *string;        /* String containing new geometry.  Has the
  3196.                  * standard form "=wxh+x+y". */
  3197.     TkWindow *winPtr;        /* Pointer to top-level window whose
  3198.                  * geometry is to be changed. */
  3199. {
  3200.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  3201.     int x, y, width, height, flags;
  3202.     char *end;
  3203.     register char *p = string;
  3204.  
  3205.     /*
  3206.      * The leading "=" is optional.
  3207.      */
  3208.  
  3209.     if (*p == '=') {
  3210.     p++;
  3211.     }
  3212.  
  3213.     /*
  3214.      * Parse the width and height, if they are present.  Don't
  3215.      * actually update any of the fields of wmPtr until we've
  3216.      * successfully parsed the entire geometry string.
  3217.      */
  3218.  
  3219.     width = wmPtr->width;
  3220.     height = wmPtr->height;
  3221.     x = wmPtr->x;
  3222.     y = wmPtr->y;
  3223.     flags = wmPtr->flags;
  3224.     if (isdigit(UCHAR(*p))) {
  3225.     width = strtoul(p, &end, 10);
  3226.     p = end;
  3227.     if (*p != 'x') {
  3228.         goto error;
  3229.     }
  3230.     p++;
  3231.     if (!isdigit(UCHAR(*p))) {
  3232.         goto error;
  3233.     }
  3234.     height = strtoul(p, &end, 10);
  3235.     p = end;
  3236.     }
  3237.  
  3238.     /*
  3239.      * Parse the X and Y coordinates, if they are present.
  3240.      */
  3241.  
  3242.     if (*p != '\0') {
  3243.     flags &= ~(WM_NEGATIVE_X | WM_NEGATIVE_Y);
  3244.     if (*p == '-') {
  3245.         flags |= WM_NEGATIVE_X;
  3246.     } else if (*p != '+') {
  3247.         goto error;
  3248.     }
  3249.     p++;
  3250.     if (!isdigit(UCHAR(*p)) && (*p != '-')) {
  3251.         goto error;
  3252.     }
  3253.     x = strtol(p, &end, 10);
  3254.     p = end;
  3255.     if (*p == '-') {
  3256.         flags |= WM_NEGATIVE_Y;
  3257.     } else if (*p != '+') {
  3258.         goto error;
  3259.     }
  3260.     p++;
  3261.     if (!isdigit(UCHAR(*p)) && (*p != '-')) {
  3262.         goto error;
  3263.     }
  3264.     y = strtol(p, &end, 10);
  3265.     if (*end != '\0') {
  3266.         goto error;
  3267.     }
  3268.  
  3269.     /*
  3270.      * Assume that the geometry information came from the user,
  3271.      * unless an explicit source has been specified.  Otherwise
  3272.      * most window managers assume that the size hints were
  3273.      * program-specified and they ignore them.
  3274.      */
  3275.  
  3276.     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
  3277.         wmPtr->sizeHintsFlags |= USPosition;
  3278.         flags |= WM_UPDATE_SIZE_HINTS;
  3279.     }
  3280.     }
  3281.  
  3282.     /*
  3283.      * Everything was parsed OK.  Update the fields of *wmPtr and
  3284.      * arrange for the appropriate information to be percolated out
  3285.      * to the window manager at the next idle moment.
  3286.      */
  3287.  
  3288.     wmPtr->width = width;
  3289.     wmPtr->height = height;
  3290.     wmPtr->x = x;
  3291.     wmPtr->y = y;
  3292.     flags |= WM_MOVE_PENDING;
  3293.     wmPtr->flags = flags;
  3294.  
  3295.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  3296.     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) winPtr);
  3297.     wmPtr->flags |= WM_UPDATE_PENDING;
  3298.     }
  3299.     return TCL_OK;
  3300.  
  3301.     error:
  3302.     Tcl_AppendResult(interp, "bad geometry specifier \"",
  3303.         string, "\"", (char *) NULL);
  3304.     return TCL_ERROR;
  3305. }
  3306.  
  3307. /*
  3308.  *----------------------------------------------------------------------
  3309.  *
  3310.  * Tk_GetRootCoords --
  3311.  *
  3312.  *    Given a token for a window, this procedure traces through the
  3313.  *    window's lineage to find the (virtual) root-window coordinates
  3314.  *    corresponding to point (0,0) in the window.
  3315.  *
  3316.  * Results:
  3317.  *    The locations pointed to by xPtr and yPtr are filled in with
  3318.  *    the root coordinates of the (0,0) point in tkwin.  If a virtual
  3319.  *    root window is in effect for the window, then the coordinates
  3320.  *    in the virtual root are returned.
  3321.  *
  3322.  * Side effects:
  3323.  *    None.
  3324.  *
  3325.  *----------------------------------------------------------------------
  3326.  */
  3327.  
  3328. void
  3329. Tk_GetRootCoords(tkwin, xPtr, yPtr)
  3330.     Tk_Window tkwin;        /* Token for window. */
  3331.     int *xPtr;            /* Where to store x-displacement of (0,0). */
  3332.     int *yPtr;            /* Where to store y-displacement of (0,0). */
  3333. {
  3334.     int x, y;
  3335.     register TkWindow *winPtr = (TkWindow *) tkwin;
  3336.  
  3337.     /*
  3338.      * Search back through this window's parents all the way to a
  3339.      * top-level window, combining the offsets of each window within
  3340.      * its parent.
  3341.      */
  3342.  
  3343.     x = y = 0;
  3344.     while (1) {
  3345.     x += winPtr->changes.x + winPtr->changes.border_width;
  3346.     y += winPtr->changes.y + winPtr->changes.border_width;
  3347.     if ((winPtr->wmInfoPtr != NULL)
  3348.         && (winPtr->wmInfoPtr->menubar == (Tk_Window) winPtr)) {
  3349.         /*
  3350.          * This window is a special menubar; switch over to its
  3351.          * associated toplevel, compensate for their differences in
  3352.          * y coordinates, then continue with the toplevel (in case
  3353.          * it's embedded).
  3354.          */
  3355.  
  3356.         y -= winPtr->wmInfoPtr->menuHeight;
  3357.         winPtr = winPtr->wmInfoPtr->winPtr;
  3358.         continue;
  3359.     }
  3360.     if (winPtr->flags & TK_TOP_LEVEL) {
  3361.         TkWindow *otherPtr;
  3362.  
  3363.         if (!(winPtr->flags & TK_EMBEDDED)) {
  3364.         break;
  3365.         }
  3366.         otherPtr = TkpGetOtherWindow(winPtr);
  3367.         if (otherPtr == NULL) {
  3368.         /*
  3369.          * The container window is not in the same application.
  3370.          * Query the X server.
  3371.          */
  3372.  
  3373.         Window root, dummyChild;
  3374.         int rootX, rootY;
  3375.  
  3376.         root = winPtr->wmInfoPtr->vRoot;
  3377.         if (root == None) {
  3378.             root = RootWindowOfScreen(Tk_Screen((Tk_Window)winPtr));
  3379.         }
  3380.         XTranslateCoordinates(winPtr->display, winPtr->window, 
  3381.             root, 0, 0, &rootX, &rootY, &dummyChild);
  3382.         x += rootX;
  3383.         y += rootY;
  3384.         break;
  3385.         } else {
  3386.         /*
  3387.          * The container window is in the same application.
  3388.          * Let's query its coordinates.
  3389.          */
  3390.  
  3391.         winPtr = otherPtr;
  3392.         continue;
  3393.         }
  3394.     }
  3395.     winPtr = winPtr->parentPtr;
  3396.     if (winPtr == NULL) {
  3397.         break;
  3398.     }
  3399.     }
  3400.     *xPtr = x;
  3401.     *yPtr = y;
  3402. }
  3403.  
  3404. /*
  3405.  *----------------------------------------------------------------------
  3406.  *
  3407.  * Tk_CoordsToWindow --
  3408.  *
  3409.  *    Given the (virtual) root coordinates of a point, this procedure
  3410.  *    returns the token for the top-most window covering that point,
  3411.  *    if there exists such a window in this application.
  3412.  *
  3413.  * Results:
  3414.  *    The return result is either a token for the window corresponding
  3415.  *    to rootX and rootY, or else NULL to indicate that there is no such
  3416.  *    window.
  3417.  *
  3418.  * Side effects:
  3419.  *    None.
  3420.  *
  3421.  *----------------------------------------------------------------------
  3422.  */
  3423.  
  3424. Tk_Window
  3425. Tk_CoordsToWindow(rootX, rootY, tkwin)
  3426.     int rootX, rootY;        /* Coordinates of point in root window.  If
  3427.                  * a virtual-root window manager is in use,
  3428.                  * these coordinates refer to the virtual
  3429.                  * root, not the real root. */
  3430.     Tk_Window tkwin;        /* Token for any window in application;
  3431.                  * used to identify the display. */
  3432. {
  3433.     Window window, parent, child;
  3434.     int x, y, childX, childY, tmpx, tmpy, bd;
  3435.     WmInfo *wmPtr;
  3436.     TkWindow *winPtr, *childPtr, *nextPtr;
  3437.  
  3438.     /*
  3439.      * Step 1: scan the list of toplevel windows to see if there is a
  3440.      * virtual root for the screen we're interested in.  If so, we have
  3441.      * to translate the coordinates from virtual root to root
  3442.      * coordinates.
  3443.      */
  3444.  
  3445.     parent = window = RootWindowOfScreen(Tk_Screen(tkwin));
  3446.     x = rootX;
  3447.     y = rootY;
  3448.     for (wmPtr = firstWmPtr; wmPtr != NULL; wmPtr = wmPtr->nextPtr) {
  3449.     if (Tk_Screen(wmPtr->winPtr) != Tk_Screen(tkwin)) {
  3450.         continue;
  3451.     }
  3452.     if (wmPtr->vRoot == None) {
  3453.         continue;
  3454.     }
  3455.     UpdateVRootGeometry(wmPtr);
  3456.     parent = wmPtr->vRoot;
  3457.     break;
  3458.     }
  3459.  
  3460.     /*
  3461.      * Step 2: work down through the window hierarchy starting at the
  3462.      * root. For each window, find the child that contains the given
  3463.      * point and then see if this child is either a wrapper for one of
  3464.      * our toplevel windows or a window manager decoration window for
  3465.      * one of our toplevels.  This approach handles several tricky
  3466.      * cases:
  3467.      *
  3468.      * 1. There may be a virtual root window between the root and one of
  3469.      *    our toplevels.
  3470.      * 2. If a toplevel is embedded, we may have to search through the
  3471.      *    windows of the container application(s) before getting to
  3472.      *    the toplevel.
  3473.      */
  3474.  
  3475.     while (1) {
  3476.     if (XTranslateCoordinates(Tk_Display(tkwin), parent, window,
  3477.         x, y, &childX, &childY, &child) == False) {
  3478.         panic("Tk_CoordsToWindow got False return from XTranslateCoordinates");
  3479.     }
  3480.     if (child == None) {
  3481.         return NULL;
  3482.     }
  3483.     for (wmPtr = firstWmPtr; wmPtr != NULL; wmPtr = wmPtr->nextPtr) {
  3484.         if (wmPtr->reparent == child) {
  3485.         goto gotToplevel;
  3486.         }
  3487.         if (wmPtr->wrapperPtr != NULL) {
  3488.         if (child == wmPtr->wrapperPtr->window) {
  3489.             goto gotToplevel;
  3490.         }
  3491.         } else if (child == wmPtr->winPtr->window) {
  3492.         goto gotToplevel;
  3493.         }
  3494.     }
  3495.     x = childX;
  3496.     y = childY;
  3497.     parent = window;
  3498.     window = child;
  3499.     }
  3500.  
  3501.     gotToplevel:
  3502.     winPtr = wmPtr->winPtr;
  3503.     if (winPtr->mainPtr != ((TkWindow *) tkwin)->mainPtr) {
  3504.     return NULL;
  3505.     }
  3506.  
  3507.     /*
  3508.      * Step 3: at this point winPtr and wmPtr refer to the toplevel that
  3509.      * contains the given coordinates, and childX and childY give the
  3510.      * translated coordinates in the *parent* of the toplevel.  Now
  3511.      * decide whether the coordinates are in the menubar or the actual
  3512.      * toplevel, and translate the coordinates into the coordinate
  3513.      * system of that window.
  3514.      */
  3515.  
  3516.     x = childX - winPtr->changes.x;
  3517.     y = childY - winPtr->changes.y;
  3518.     if ((x < 0) || (x >= winPtr->changes.width)
  3519.         || (y >= winPtr->changes.height)) {
  3520.     return NULL;
  3521.     }
  3522.     if (y < 0) {
  3523.     winPtr = (TkWindow *) wmPtr->menubar;
  3524.     if (winPtr == NULL) {
  3525.         return NULL;
  3526.     }
  3527.     y += wmPtr->menuHeight;
  3528.     if (y < 0) {
  3529.         return NULL;
  3530.     }
  3531.     }
  3532.  
  3533.     /*
  3534.      * Step 4: work down through the hierarchy underneath the current
  3535.      * window. At each level, scan through all the children to find the
  3536.      * highest one in the stacking order that contains the point.  Then
  3537.      * repeat the whole process on that child.
  3538.      */
  3539.  
  3540.     while (1) {
  3541.     nextPtr = NULL;
  3542.     for (childPtr = winPtr->childList; childPtr != NULL;
  3543.         childPtr = childPtr->nextPtr) {
  3544.         if (!Tk_IsMapped(childPtr) || (childPtr->flags & TK_TOP_LEVEL)) {
  3545.         continue;
  3546.         }
  3547.         if (childPtr->flags & TK_REPARENTED) {
  3548.         continue;
  3549.         }
  3550.         tmpx = x - childPtr->changes.x;
  3551.         tmpy = y - childPtr->changes.y;
  3552.         bd = childPtr->changes.border_width;
  3553.         if ((tmpx >= -bd) && (tmpy >= -bd)
  3554.             && (tmpx < (childPtr->changes.width + bd))
  3555.             && (tmpy < (childPtr->changes.height + bd))) {
  3556.         nextPtr = childPtr;
  3557.         }
  3558.     }
  3559.     if (nextPtr == NULL) {
  3560.         break;
  3561.     }
  3562.     winPtr = nextPtr;
  3563.     x -= winPtr->changes.x;
  3564.     y -= winPtr->changes.y;
  3565.     if ((winPtr->flags & TK_CONTAINER)
  3566.         && (winPtr->flags & TK_BOTH_HALVES)) {
  3567.         /*
  3568.          * The window containing the point is a container, and the
  3569.          * embedded application is in this same process.  Switch
  3570.          * over to the toplevel for the embedded application and
  3571.          * start processing that toplevel from scratch.
  3572.          */
  3573.  
  3574.         winPtr = TkpGetOtherWindow(winPtr);
  3575.         wmPtr = winPtr->wmInfoPtr;
  3576.         childX = x;
  3577.         childY = y;
  3578.         goto gotToplevel;
  3579.     }
  3580.     }
  3581.     return (Tk_Window) winPtr;
  3582. }
  3583.  
  3584. /*
  3585.  *----------------------------------------------------------------------
  3586.  *
  3587.  * UpdateVRootGeometry --
  3588.  *
  3589.  *    This procedure is called to update all the virtual root
  3590.  *    geometry information in wmPtr.
  3591.  *
  3592.  * Results:
  3593.  *    None.
  3594.  *
  3595.  * Side effects:
  3596.  *    The vRootX, vRootY, vRootWidth, and vRootHeight fields in
  3597.  *    wmPtr are filled with the most up-to-date information.
  3598.  *
  3599.  *----------------------------------------------------------------------
  3600.  */
  3601.  
  3602. static void
  3603. UpdateVRootGeometry(wmPtr)
  3604.     WmInfo *wmPtr;        /* Window manager information to be
  3605.                  * updated.  The wmPtr->vRoot field must
  3606.                  * be valid. */
  3607. {
  3608.     TkWindow *winPtr = wmPtr->winPtr;
  3609.     int bd;
  3610.     unsigned int dummy;
  3611.     Window dummy2;
  3612.     Status status;
  3613.     Tk_ErrorHandler handler;
  3614.  
  3615.     /*
  3616.      * If this isn't a virtual-root window manager, just return information
  3617.      * about the screen.
  3618.      */
  3619.  
  3620.     wmPtr->flags &= ~WM_VROOT_OFFSET_STALE;
  3621.     if (wmPtr->vRoot == None) {
  3622.     noVRoot:
  3623.     wmPtr->vRootX = wmPtr->vRootY = 0;
  3624.     wmPtr->vRootWidth = DisplayWidth(winPtr->display, winPtr->screenNum);
  3625.     wmPtr->vRootHeight = DisplayHeight(winPtr->display, winPtr->screenNum);
  3626.     return;
  3627.     }
  3628.  
  3629.     /*
  3630.      * Refresh the virtual root information if it's out of date.
  3631.      */
  3632.  
  3633.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  3634.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  3635.     status = XGetGeometry(winPtr->display, wmPtr->vRoot,
  3636.         &dummy2, &wmPtr->vRootX, &wmPtr->vRootY,
  3637.         (unsigned int *) &wmPtr->vRootWidth,
  3638.         (unsigned int *) &wmPtr->vRootHeight, (unsigned int *) &bd,
  3639.         &dummy);
  3640.     if (wmTracing) {
  3641.     printf("UpdateVRootGeometry: x = %d, y = %d, width = %d, ",
  3642.         wmPtr->vRootX, wmPtr->vRootY, wmPtr->vRootWidth);
  3643.     printf("height = %d, status = %d\n", wmPtr->vRootHeight, status);
  3644.     }
  3645.     Tk_DeleteErrorHandler(handler);
  3646.     if (status == 0) {
  3647.     /*
  3648.      * The virtual root is gone!  Pretend that it never existed.
  3649.      */
  3650.  
  3651.     wmPtr->vRoot = None;
  3652.     goto noVRoot;
  3653.     }
  3654. }
  3655.  
  3656. /*
  3657.  *----------------------------------------------------------------------
  3658.  *
  3659.  * Tk_GetVRootGeometry --
  3660.  *
  3661.  *    This procedure returns information about the virtual root
  3662.  *    window corresponding to a particular Tk window.
  3663.  *
  3664.  * Results:
  3665.  *    The values at xPtr, yPtr, widthPtr, and heightPtr are set
  3666.  *    with the offset and dimensions of the root window corresponding
  3667.  *    to tkwin.  If tkwin is being managed by a virtual root window
  3668.  *    manager these values correspond to the virtual root window being
  3669.  *    used for tkwin;  otherwise the offsets will be 0 and the
  3670.  *    dimensions will be those of the screen.
  3671.  *
  3672.  * Side effects:
  3673.  *    Vroot window information is refreshed if it is out of date.
  3674.  *
  3675.  *----------------------------------------------------------------------
  3676.  */
  3677.  
  3678. void
  3679. Tk_GetVRootGeometry(tkwin, xPtr, yPtr, widthPtr, heightPtr)
  3680.     Tk_Window tkwin;        /* Window whose virtual root is to be
  3681.                  * queried. */
  3682.     int *xPtr, *yPtr;        /* Store x and y offsets of virtual root
  3683.                  * here. */
  3684.     int *widthPtr, *heightPtr;    /* Store dimensions of virtual root here. */
  3685. {
  3686.     WmInfo *wmPtr;
  3687.     TkWindow *winPtr = (TkWindow *) tkwin;
  3688.  
  3689.     /*
  3690.      * Find the top-level window for tkwin, and locate the window manager
  3691.      * information for that window.
  3692.      */
  3693.  
  3694.     while (!(winPtr->flags & TK_TOP_LEVEL) && (winPtr->parentPtr != NULL)) {
  3695.     winPtr = winPtr->parentPtr;
  3696.     }
  3697.     wmPtr = winPtr->wmInfoPtr;
  3698.  
  3699.     /*
  3700.      * Make sure that the geometry information is up-to-date, then copy
  3701.      * it out to the caller.
  3702.      */
  3703.  
  3704.     if (wmPtr->flags & WM_VROOT_OFFSET_STALE) {
  3705.     UpdateVRootGeometry(wmPtr);
  3706.     }
  3707.     *xPtr = wmPtr->vRootX;
  3708.     *yPtr = wmPtr->vRootY;
  3709.     *widthPtr = wmPtr->vRootWidth;
  3710.     *heightPtr = wmPtr->vRootHeight;
  3711. }
  3712.  
  3713. /*
  3714.  *----------------------------------------------------------------------
  3715.  *
  3716.  * Tk_MoveToplevelWindow --
  3717.  *
  3718.  *    This procedure is called instead of Tk_MoveWindow to adjust
  3719.  *    the x-y location of a top-level window.  It delays the actual
  3720.  *    move to a later time and keeps window-manager information
  3721.  *    up-to-date with the move
  3722.  *
  3723.  * Results:
  3724.  *    None.
  3725.  *
  3726.  * Side effects:
  3727.  *    The window is eventually moved so that its upper-left corner
  3728.  *    (actually, the upper-left corner of the window's decorative
  3729.  *    frame, if there is one) is at (x,y).
  3730.  *
  3731.  *----------------------------------------------------------------------
  3732.  */
  3733.  
  3734. void
  3735. Tk_MoveToplevelWindow(tkwin, x, y)
  3736.     Tk_Window tkwin;        /* Window to move. */
  3737.     int x, y;            /* New location for window (within
  3738.                  * parent). */
  3739. {
  3740.     TkWindow *winPtr = (TkWindow *) tkwin;
  3741.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  3742.  
  3743.     if (!(winPtr->flags & TK_TOP_LEVEL)) {
  3744.     panic("Tk_MoveToplevelWindow called with non-toplevel window");
  3745.     }
  3746.     wmPtr->x = x;
  3747.     wmPtr->y = y;
  3748.     wmPtr->flags |= WM_MOVE_PENDING;
  3749.     wmPtr->flags &= ~(WM_NEGATIVE_X|WM_NEGATIVE_Y);
  3750.     if ((wmPtr->sizeHintsFlags & (USPosition|PPosition)) == 0) {
  3751.     wmPtr->sizeHintsFlags |= USPosition;
  3752.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  3753.     }
  3754.  
  3755.     /*
  3756.      * If the window has already been mapped, must bring its geometry
  3757.      * up-to-date immediately, otherwise an event might arrive from the
  3758.      * server that would overwrite wmPtr->x and wmPtr->y and lose the
  3759.      * new position.
  3760.      */
  3761.  
  3762.     if (!(wmPtr->flags & WM_NEVER_MAPPED)) {
  3763.     if (wmPtr->flags & WM_UPDATE_PENDING) {
  3764.         Tcl_CancelIdleCall(UpdateGeometryInfo, (ClientData) winPtr);
  3765.     }
  3766.     UpdateGeometryInfo((ClientData) winPtr);
  3767.     }
  3768. }
  3769.  
  3770. /*
  3771.  *----------------------------------------------------------------------
  3772.  *
  3773.  * UpdateWmProtocols --
  3774.  *
  3775.  *    This procedure transfers the most up-to-date information about
  3776.  *    window manager protocols from the WmInfo structure to the actual
  3777.  *    property on the top-level window.
  3778.  *
  3779.  * Results:
  3780.  *    None.
  3781.  *
  3782.  * Side effects:
  3783.  *    The WM_PROTOCOLS property gets changed for wmPtr's window.
  3784.  *
  3785.  *----------------------------------------------------------------------
  3786.  */
  3787.  
  3788. static void
  3789. UpdateWmProtocols(wmPtr)
  3790.     register WmInfo *wmPtr;    /* Information about top-level window. */
  3791. {
  3792.     register ProtocolHandler *protPtr;
  3793.     Atom deleteWindowAtom;
  3794.     int count;
  3795.     Atom *arrayPtr, *atomPtr;
  3796.  
  3797.     /*
  3798.      * There are only two tricky parts here.  First, there could be any
  3799.      * number of atoms for the window, so count them and malloc an array
  3800.      * to hold all of their atoms.  Second, we *always* want to respond
  3801.      * to the WM_DELETE_WINDOW protocol, even if no-one's officially asked.
  3802.      */
  3803.  
  3804.     for (protPtr = wmPtr->protPtr, count = 1; protPtr != NULL;
  3805.         protPtr = protPtr->nextPtr, count++) {
  3806.     /* Empty loop body;  we're just counting the handlers. */
  3807.     }
  3808.     arrayPtr = (Atom *) ckalloc((unsigned) (count * sizeof(Atom)));
  3809.     deleteWindowAtom = Tk_InternAtom((Tk_Window) wmPtr->winPtr,
  3810.         "WM_DELETE_WINDOW");
  3811.     arrayPtr[0] = deleteWindowAtom;
  3812.     for (protPtr = wmPtr->protPtr, atomPtr = &arrayPtr[1];
  3813.         protPtr != NULL; protPtr = protPtr->nextPtr) {
  3814.     if (protPtr->protocol != deleteWindowAtom) {
  3815.         *atomPtr = protPtr->protocol;
  3816.         atomPtr++;
  3817.     }
  3818.     }
  3819.     XChangeProperty(wmPtr->winPtr->display, wmPtr->wrapperPtr->window,
  3820.         Tk_InternAtom((Tk_Window) wmPtr->winPtr, "WM_PROTOCOLS"),
  3821.         XA_ATOM, 32, PropModeReplace, (unsigned char *) arrayPtr,
  3822.         atomPtr-arrayPtr);
  3823.     ckfree((char *) arrayPtr);
  3824. }
  3825.  
  3826. /*
  3827.  *----------------------------------------------------------------------
  3828.  *
  3829.  * TkWmProtocolEventProc --
  3830.  *
  3831.  *    This procedure is called by the Tk_HandleEvent whenever a
  3832.  *    ClientMessage event arrives whose type is "WM_PROTOCOLS".
  3833.  *    This procedure handles the message from the window manager
  3834.  *    in an appropriate fashion.
  3835.  *
  3836.  * Results:
  3837.  *    None.
  3838.  *
  3839.  * Side effects:
  3840.  *    Depends on what sort of handler, if any, was set up for the
  3841.  *    protocol.
  3842.  *
  3843.  *----------------------------------------------------------------------
  3844.  */
  3845.  
  3846. void
  3847. TkWmProtocolEventProc(winPtr, eventPtr)
  3848.     TkWindow *winPtr;        /* Window to which the event was sent. */
  3849.     XEvent *eventPtr;        /* X event. */
  3850. {
  3851.     WmInfo *wmPtr;
  3852.     register ProtocolHandler *protPtr;
  3853.     Atom protocol;
  3854.     int result;
  3855.     char *protocolName;
  3856.     Tcl_Interp *interp;
  3857.  
  3858.     wmPtr = winPtr->wmInfoPtr;
  3859.     if (wmPtr == NULL) {
  3860.     return;
  3861.     }
  3862.     protocol = (Atom) eventPtr->xclient.data.l[0];
  3863.  
  3864.     /*
  3865.      * Note: it's very important to retrieve the protocol name now,
  3866.      * before invoking the command, even though the name won't be used
  3867.      * until after the command returns.  This is because the command
  3868.      * could delete winPtr, making it impossible for us to use it
  3869.      * later in the call to Tk_GetAtomName.
  3870.      */
  3871.  
  3872.     protocolName = Tk_GetAtomName((Tk_Window) winPtr, protocol);
  3873.     for (protPtr = wmPtr->protPtr; protPtr != NULL;
  3874.         protPtr = protPtr->nextPtr) {
  3875.     if (protocol == protPtr->protocol) {
  3876.         Tcl_Preserve((ClientData) protPtr);
  3877.             interp = protPtr->interp;
  3878.             Tcl_Preserve((ClientData) interp);
  3879.         result = Tcl_GlobalEval(interp, protPtr->command);
  3880.         if (result != TCL_OK) {
  3881.         Tcl_AddErrorInfo(interp, "\n    (command for \"");
  3882.         Tcl_AddErrorInfo(interp, protocolName);
  3883.         Tcl_AddErrorInfo(interp,
  3884.             "\" window manager protocol)");
  3885.         Tcl_BackgroundError(interp);
  3886.         }
  3887.             Tcl_Release((ClientData) interp);
  3888.         Tcl_Release((ClientData) protPtr);
  3889.         return;
  3890.     }
  3891.     }
  3892.  
  3893.     /*
  3894.      * No handler was present for this protocol.  If this is a
  3895.      * WM_DELETE_WINDOW message then just destroy the window.
  3896.      */
  3897.  
  3898.     if (protocol == Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW")) {
  3899.     Tk_DestroyWindow((Tk_Window) wmPtr->winPtr);
  3900.     }
  3901. }
  3902.  
  3903. /*
  3904.  *----------------------------------------------------------------------
  3905.  *
  3906.  * TkWmRestackToplevel --
  3907.  *
  3908.  *    This procedure restacks a top-level window.
  3909.  *
  3910.  * Results:
  3911.  *    None.
  3912.  *
  3913.  * Side effects:
  3914.  *    WinPtr gets restacked  as specified by aboveBelow and otherPtr.
  3915.  *    This procedure doesn't return until the restack has taken
  3916.  *    effect and the ConfigureNotify event for it has been received.
  3917.  *
  3918.  *----------------------------------------------------------------------
  3919.  */
  3920.  
  3921. void
  3922. TkWmRestackToplevel(winPtr, aboveBelow, otherPtr)
  3923.     TkWindow *winPtr;        /* Window to restack. */
  3924.     int aboveBelow;        /* Gives relative position for restacking;
  3925.                  * must be Above or Below. */
  3926.     TkWindow *otherPtr;        /* Window relative to which to restack;
  3927.                  * if NULL, then winPtr gets restacked
  3928.                  * above or below *all* siblings. */
  3929. {
  3930.     XWindowChanges changes;
  3931.     XWindowAttributes atts;
  3932.     unsigned int mask;
  3933.     Window window, dummy1, dummy2, vRoot;
  3934.     Window *children;
  3935.     unsigned int numChildren;
  3936.     int i;
  3937.     int desiredIndex = 0;    /* Initialized to stop gcc warnings. */
  3938.     int ourIndex = 0;        /* Initialized to stop gcc warnings. */
  3939.     unsigned long serial;
  3940.     XEvent event;
  3941.     int diff;
  3942.     Tk_ErrorHandler handler;
  3943.     TkWindow *wrapperPtr;
  3944.  
  3945.     changes.stack_mode = aboveBelow;
  3946.     changes.sibling = None;
  3947.     mask = CWStackMode;
  3948.     if (winPtr->window == None) {
  3949.     Tk_MakeWindowExist((Tk_Window) winPtr);
  3950.     }
  3951.     if (winPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
  3952.     /*
  3953.      * Can't set stacking order properly until the window is on the
  3954.      * screen (mapping it may give it a reparent window), so make sure
  3955.      * it's on the screen.
  3956.      */
  3957.  
  3958.     TkWmMapWindow(winPtr);
  3959.     }
  3960.     wrapperPtr = winPtr->wmInfoPtr->wrapperPtr;
  3961.     window = (winPtr->wmInfoPtr->reparent != None)
  3962.         ? winPtr->wmInfoPtr->reparent : wrapperPtr->window;
  3963.     if (otherPtr != NULL) {
  3964.     if (otherPtr->window == None) {
  3965.         Tk_MakeWindowExist((Tk_Window) otherPtr);
  3966.     }
  3967.     if (otherPtr->wmInfoPtr->flags & WM_NEVER_MAPPED) {
  3968.         TkWmMapWindow(otherPtr);
  3969.     }
  3970.     changes.sibling = (otherPtr->wmInfoPtr->reparent != None)
  3971.         ? otherPtr->wmInfoPtr->reparent
  3972.         : otherPtr->wmInfoPtr->wrapperPtr->window;
  3973.     mask = CWStackMode|CWSibling;
  3974.     }
  3975.  
  3976.     /*
  3977.      * Before actually reconfiguring the window, see if it's already
  3978.      * in the right place.  If so then don't reconfigure it.  The
  3979.      * reason for this extra work is that some window managers will
  3980.      * ignore the reconfigure request if the window is already in
  3981.      * the right place, causing a long delay in WaitForConfigureNotify
  3982.      * while it times out.  Special note: if the window is almost in
  3983.      * the right place, and the only windows between it and the right
  3984.      * place aren't mapped, then we don't reconfigure it either, for
  3985.      * the same reason.
  3986.      */
  3987.  
  3988.     vRoot = winPtr->wmInfoPtr->vRoot;
  3989.     if (vRoot == None) {
  3990.     vRoot = RootWindowOfScreen(Tk_Screen((Tk_Window) winPtr));
  3991.     }
  3992.     if (XQueryTree(winPtr->display, vRoot, &dummy1, &dummy2,
  3993.         &children, &numChildren) != 0) {
  3994.     /*
  3995.      * Find where our window is in the stacking order, and
  3996.      * compute the desired location in the stacking order.
  3997.      */
  3998.  
  3999.     for (i = 0; i < numChildren; i++) {
  4000.         if (children[i] == window) {
  4001.         ourIndex = i;
  4002.         }
  4003.         if (children[i] == changes.sibling) {
  4004.         desiredIndex = i;
  4005.         }
  4006.     }
  4007.     if (mask & CWSibling) {
  4008.         if (aboveBelow == Above) {
  4009.         if (desiredIndex < ourIndex) {
  4010.             desiredIndex += 1;
  4011.         }
  4012.         } else {
  4013.         if (desiredIndex > ourIndex) {
  4014.             desiredIndex -= 1;
  4015.         }
  4016.         }
  4017.     } else {
  4018.         if (aboveBelow == Above) {
  4019.         desiredIndex = numChildren-1;
  4020.         } else {
  4021.         desiredIndex = 0;
  4022.         }
  4023.     }
  4024.  
  4025.     /*
  4026.      * See if there are any mapped windows between where we are
  4027.      * and where we want to be.
  4028.      */
  4029.  
  4030.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  4031.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  4032.     while (desiredIndex != ourIndex) {
  4033.         if ((XGetWindowAttributes(winPtr->display, children[desiredIndex],
  4034.             &atts) != 0) && (atts.map_state != IsUnmapped)) {
  4035.         break;
  4036.         }
  4037.         if (desiredIndex < ourIndex) {
  4038.         desiredIndex++;
  4039.         } else {
  4040.         desiredIndex--;
  4041.         }
  4042.     }
  4043.     Tk_DeleteErrorHandler(handler);
  4044.     XFree((char *) children);
  4045.     if (ourIndex == desiredIndex) {
  4046.         return;
  4047.     }
  4048.     }
  4049.  
  4050.     /*
  4051.      * Reconfigure the window.  This tricky because of two things:
  4052.      * (a) Some window managers, like olvwm, insist that we raise
  4053.      *     or lower the toplevel window itself, as opposed to its
  4054.      *     decorative frame.  Attempts to raise or lower the frame
  4055.      *     are ignored.
  4056.      * (b) If the raise or lower is relative to a sibling, X will
  4057.      *     generate an error unless we work with the frames (the
  4058.      *     toplevels themselves aren't siblings).
  4059.      * Fortunately, the procedure XReconfigureWMWindow is supposed
  4060.      * to handle all of this stuff, so be careful to use it instead
  4061.      * of XConfigureWindow.
  4062.      */
  4063.  
  4064.     serial = NextRequest(winPtr->display);
  4065.     if (window != wrapperPtr->window) {
  4066.     /*
  4067.      * We're going to have to wait for events on a window that
  4068.      * Tk doesn't own, so we have to tell X specially that we
  4069.      * want to get events on that window.  To make matters worse,
  4070.      * it's possible that the window doesn't exist anymore (e.g.
  4071.      * the toplevel could have been withdrawn) so ignore events
  4072.      * occurring during the request.
  4073.      */
  4074.  
  4075.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  4076.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  4077.     XSelectInput(winPtr->display, window, StructureNotifyMask);
  4078.     Tk_DeleteErrorHandler(handler);
  4079.     }
  4080.     XReconfigureWMWindow(winPtr->display, wrapperPtr->window,
  4081.         Tk_ScreenNumber((Tk_Window) winPtr), mask,  &changes);
  4082.  
  4083.     /*
  4084.      * Wait for the reconfiguration to complete.  If we don't wait, then
  4085.      * the window may not restack for a while and the application might
  4086.      * observe it before it has restacked.  Waiting for the reconfiguration
  4087.      * is tricky if winPtr has been reparented, since the window getting
  4088.      * the event isn't one that Tk owns.
  4089.      */
  4090.  
  4091.     if (window == wrapperPtr->window) {
  4092.     WaitForConfigureNotify(winPtr, serial);
  4093.     } else {
  4094.     while (1) {
  4095.         if (WaitForEvent(winPtr->display, window, ConfigureNotify,
  4096.             &event) != TCL_OK) {
  4097.         break;
  4098.         }
  4099.         diff = event.xconfigure.serial - serial;
  4100.         if (diff >= 0) {
  4101.         break;
  4102.         }
  4103.     }
  4104.  
  4105.     /*
  4106.      * Ignore errors that occur when we are de-selecting events on
  4107.      * window, since it's possible that the window doesn't exist
  4108.      * anymore (see comment above previous call to XSelectInput).
  4109.      */
  4110.  
  4111.     handler = Tk_CreateErrorHandler(winPtr->display, -1, -1, -1,
  4112.         (Tk_ErrorProc *) NULL, (ClientData) NULL);
  4113.     XSelectInput(winPtr->display, window, (long) 0);
  4114.     Tk_DeleteErrorHandler(handler);
  4115.     }
  4116. }
  4117.  
  4118. /*
  4119.  *----------------------------------------------------------------------
  4120.  *
  4121.  * TkWmAddToColormapWindows --
  4122.  *
  4123.  *    This procedure is called to add a given window to the
  4124.  *    WM_COLORMAP_WINDOWS property for its top-level, if it
  4125.  *    isn't already there.  It is invoked by the Tk code that
  4126.  *    creates a new colormap, in order to make sure that colormap
  4127.  *    information is propagated to the window manager by default.
  4128.  *
  4129.  * Results:
  4130.  *    None.
  4131.  *
  4132.  * Side effects:
  4133.  *    WinPtr's window gets added to the WM_COLORMAP_WINDOWS
  4134.  *    property of its nearest top-level ancestor, unless the
  4135.  *    colormaps have been set explicitly with the
  4136.  *    "wm colormapwindows" command.
  4137.  *
  4138.  *----------------------------------------------------------------------
  4139.  */
  4140.  
  4141. void
  4142. TkWmAddToColormapWindows(winPtr)
  4143.     TkWindow *winPtr;        /* Window with a non-default colormap.
  4144.                  * Should not be a top-level window. */
  4145. {
  4146.     TkWindow *wrapperPtr;
  4147.     TkWindow *topPtr;
  4148.     Window *oldPtr, *newPtr;
  4149.     int count, i;
  4150.  
  4151.     if (winPtr->window == None) {
  4152.     return;
  4153.     }
  4154.  
  4155.     for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
  4156.     if (topPtr == NULL) {
  4157.         /*
  4158.          * Window is being deleted.  Skip the whole operation.
  4159.          */
  4160.  
  4161.         return;
  4162.     }
  4163.     if (topPtr->flags & TK_TOP_LEVEL) {
  4164.         break;
  4165.     }
  4166.     }
  4167.     if (topPtr->wmInfoPtr->flags & WM_COLORMAPS_EXPLICIT) {
  4168.     return;
  4169.     }
  4170.     if (topPtr->wmInfoPtr->wrapperPtr == NULL) {
  4171.     CreateWrapper(topPtr->wmInfoPtr);
  4172.     }
  4173.     wrapperPtr = topPtr->wmInfoPtr->wrapperPtr;
  4174.  
  4175.     /*
  4176.      * Fetch the old value of the property.
  4177.      */
  4178.  
  4179.     if (XGetWMColormapWindows(topPtr->display, wrapperPtr->window,
  4180.         &oldPtr, &count) == 0) {
  4181.     oldPtr = NULL;
  4182.     count = 0;
  4183.     }
  4184.  
  4185.     /*
  4186.      * Make sure that the window isn't already in the list.
  4187.      */
  4188.  
  4189.     for (i = 0; i < count; i++) {
  4190.     if (oldPtr[i] == winPtr->window) {
  4191.         return;
  4192.     }
  4193.     }
  4194.  
  4195.     /*
  4196.      * Make a new bigger array and use it to reset the property.
  4197.      * Automatically add the toplevel itself as the last element
  4198.      * of the list.
  4199.      */
  4200.  
  4201.     newPtr = (Window *) ckalloc((unsigned) ((count+2)*sizeof(Window)));
  4202.     for (i = 0; i < count; i++) {
  4203.     newPtr[i] = oldPtr[i];
  4204.     }
  4205.     if (count == 0) {
  4206.     count++;
  4207.     }
  4208.     newPtr[count-1] = winPtr->window;
  4209.     newPtr[count] = topPtr->window;
  4210.     XSetWMColormapWindows(topPtr->display, wrapperPtr->window, newPtr,
  4211.         count+1);
  4212.     ckfree((char *) newPtr);
  4213.     if (oldPtr != NULL) {
  4214.     XFree((char *) oldPtr);
  4215.     }
  4216. }
  4217.  
  4218. /*
  4219.  *----------------------------------------------------------------------
  4220.  *
  4221.  * TkWmRemoveFromColormapWindows --
  4222.  *
  4223.  *    This procedure is called to remove a given window from the
  4224.  *    WM_COLORMAP_WINDOWS property for its top-level.  It is invoked
  4225.  *    when windows are deleted.
  4226.  *
  4227.  * Results:
  4228.  *    None.
  4229.  *
  4230.  * Side effects:
  4231.  *    WinPtr's window gets removed from the WM_COLORMAP_WINDOWS
  4232.  *    property of its nearest top-level ancestor, unless the
  4233.  *    top-level itself is being deleted too.
  4234.  *
  4235.  *----------------------------------------------------------------------
  4236.  */
  4237.  
  4238. void
  4239. TkWmRemoveFromColormapWindows(winPtr)
  4240.     TkWindow *winPtr;        /* Window that may be present in
  4241.                  * WM_COLORMAP_WINDOWS property for its
  4242.                  * top-level.  Should not be a top-level
  4243.                  * window. */
  4244. {
  4245.     TkWindow *wrapperPtr;
  4246.     TkWindow *topPtr;
  4247.     Window *oldPtr;
  4248.     int count, i, j;
  4249.  
  4250.     if (winPtr->window == None) {
  4251.     return;
  4252.     }
  4253.  
  4254.     for (topPtr = winPtr->parentPtr; ; topPtr = topPtr->parentPtr) {
  4255.     if (topPtr == NULL) {
  4256.         /*
  4257.          * Ancestors have been deleted, so skip the whole operation.
  4258.          * Seems like this can't ever happen?
  4259.          */
  4260.  
  4261.         return;
  4262.     }
  4263.     if (topPtr->flags & TK_TOP_LEVEL) {
  4264.         break;
  4265.     }
  4266.     }
  4267.     if (topPtr->flags & TK_ALREADY_DEAD) {
  4268.     /*
  4269.      * Top-level is being deleted, so there's no need to cleanup
  4270.      * the WM_COLORMAP_WINDOWS property.
  4271.      */
  4272.  
  4273.     return;
  4274.     }
  4275.     if (topPtr->wmInfoPtr->wrapperPtr == NULL) {
  4276.     CreateWrapper(topPtr->wmInfoPtr);
  4277.     }
  4278.     wrapperPtr = topPtr->wmInfoPtr->wrapperPtr;
  4279.     if (wrapperPtr == NULL) {
  4280.     return;
  4281.     }
  4282.  
  4283.     /*
  4284.      * Fetch the old value of the property.
  4285.      */
  4286.  
  4287.     if (XGetWMColormapWindows(topPtr->display, wrapperPtr->window,
  4288.         &oldPtr, &count) == 0) {
  4289.     return;
  4290.     }
  4291.  
  4292.     /*
  4293.      * Find the window and slide the following ones down to cover
  4294.      * it up.
  4295.      */
  4296.  
  4297.     for (i = 0; i < count; i++) {
  4298.     if (oldPtr[i] == winPtr->window) {
  4299.         for (j = i ; j < count-1; j++) {
  4300.         oldPtr[j] = oldPtr[j+1];
  4301.         }
  4302.         XSetWMColormapWindows(topPtr->display, wrapperPtr->window,
  4303.             oldPtr, count-1);
  4304.         break;
  4305.     }
  4306.     }
  4307.     XFree((char *) oldPtr);
  4308. }
  4309.  
  4310. /*
  4311.  *----------------------------------------------------------------------
  4312.  *
  4313.  * TkGetPointerCoords --
  4314.  *
  4315.  *    Fetch the position of the mouse pointer.
  4316.  *
  4317.  * Results:
  4318.  *    *xPtr and *yPtr are filled in with the (virtual) root coordinates
  4319.  *    of the mouse pointer for tkwin's display.  If the pointer isn't
  4320.  *    on tkwin's screen, then -1 values are returned for both
  4321.  *    coordinates.  The argument tkwin must be a toplevel window.
  4322.  *
  4323.  * Side effects:
  4324.  *    None.
  4325.  *
  4326.  *----------------------------------------------------------------------
  4327.  */
  4328.  
  4329. void
  4330. TkGetPointerCoords(tkwin, xPtr, yPtr)
  4331.     Tk_Window tkwin;        /* Toplevel window that identifies screen
  4332.                  * on which lookup is to be done. */
  4333.     int *xPtr, *yPtr;        /* Store pointer coordinates here. */
  4334. {
  4335.     TkWindow *winPtr = (TkWindow *) tkwin;
  4336.     WmInfo *wmPtr;
  4337.     Window w, root, child;
  4338.     int rootX, rootY;
  4339.     unsigned int mask;
  4340.  
  4341.     wmPtr = winPtr->wmInfoPtr;
  4342.  
  4343.     w = wmPtr->vRoot;
  4344.     if (w == None) {
  4345.     w = RootWindow(winPtr->display, winPtr->screenNum);
  4346.     }
  4347.     if (XQueryPointer(winPtr->display, w, &root, &child, &rootX, &rootY,
  4348.         xPtr, yPtr, &mask) != True) {
  4349.     *xPtr = -1;
  4350.     *yPtr = -1;
  4351.     }
  4352. }
  4353.  
  4354. /*
  4355.  *----------------------------------------------------------------------
  4356.  *
  4357.  * GetMaxSize --
  4358.  *
  4359.  *    This procedure computes the current maxWidth and maxHeight
  4360.  *    values for a window, taking into account the possibility
  4361.  *    that they may be defaulted.
  4362.  *
  4363.  * Results:
  4364.  *    The values at *maxWidthPtr and *maxHeightPtr are filled
  4365.  *    in with the maximum allowable dimensions of wmPtr's window,
  4366.  *    in grid units.  If no maximum has been specified for the
  4367.  *    window, then this procedure computes the largest sizes that
  4368.  *    will fit on the screen.
  4369.  *
  4370.  * Side effects:
  4371.  *    None.
  4372.  *
  4373.  *----------------------------------------------------------------------
  4374.  */
  4375.  
  4376. static void
  4377. GetMaxSize(wmPtr, maxWidthPtr, maxHeightPtr)
  4378.     WmInfo *wmPtr;        /* Window manager information for the
  4379.                  * window. */
  4380.     int *maxWidthPtr;        /* Where to store the current maximum
  4381.                  * width of the window. */
  4382.     int *maxHeightPtr;        /* Where to store the current maximum
  4383.                  * height of the window. */
  4384. {
  4385.     int tmp;
  4386.  
  4387.     if (wmPtr->maxWidth > 0) {
  4388.     *maxWidthPtr = wmPtr->maxWidth;
  4389.     } else {
  4390.     /*
  4391.      * Must compute a default width.  Fill up the display, leaving a
  4392.      * bit of extra space for the window manager's borders.
  4393.      */
  4394.  
  4395.     tmp = DisplayWidth(wmPtr->winPtr->display, wmPtr->winPtr->screenNum)
  4396.         - 15;
  4397.     if (wmPtr->gridWin != NULL) {
  4398.         /*
  4399.          * Gridding is turned on;  convert from pixels to grid units.
  4400.          */
  4401.  
  4402.         tmp = wmPtr->reqGridWidth
  4403.             + (tmp - wmPtr->winPtr->reqWidth)/wmPtr->widthInc;
  4404.     }
  4405.     *maxWidthPtr = tmp;
  4406.     }
  4407.     if (wmPtr->maxHeight > 0) {
  4408.     *maxHeightPtr = wmPtr->maxHeight;
  4409.     } else {
  4410.     tmp = DisplayHeight(wmPtr->winPtr->display, wmPtr->winPtr->screenNum)
  4411.         - 30;
  4412.     if (wmPtr->gridWin != NULL) {
  4413.         tmp = wmPtr->reqGridHeight
  4414.             + (tmp - wmPtr->winPtr->reqHeight)/wmPtr->heightInc;
  4415.     }
  4416.     *maxHeightPtr = tmp;
  4417.     }
  4418. }
  4419.  
  4420. /*
  4421.  *----------------------------------------------------------------------
  4422.  *
  4423.  * TkMakeMenuWindow --
  4424.  *
  4425.  *    Configure the window to be either a pull-down (or pop-up)
  4426.  *    menu, or as a toplevel (torn-off) menu or palette.
  4427.  *
  4428.  * Results:
  4429.  *    None.
  4430.  *
  4431.  * Side effects:
  4432.  *    Changes the style bit used to create a new Mac toplevel.
  4433.  *
  4434.  *----------------------------------------------------------------------
  4435.  */
  4436.  
  4437. void
  4438. TkMakeMenuWindow(tkwin, transient)
  4439.     Tk_Window tkwin;        /* New window. */
  4440.     int transient;        /* 1 means menu is only posted briefly as
  4441.                  * a popup or pulldown or cascade.  0 means
  4442.                  * menu is always visible, e.g. as a torn-off
  4443.                  * menu.  Determines whether save_under and
  4444.                  * override_redirect should be set. */
  4445. {
  4446.     WmInfo *wmPtr;
  4447.     XSetWindowAttributes atts;
  4448.     TkWindow *wrapperPtr;
  4449.  
  4450.     if (!Tk_IsTopLevel(tkwin)) {
  4451.     return;
  4452.     }
  4453.     wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;
  4454.     if (wmPtr->wrapperPtr == NULL) {
  4455.     CreateWrapper(wmPtr);
  4456.     }
  4457.     wrapperPtr = wmPtr->wrapperPtr;
  4458.     if (transient) {
  4459.     atts.override_redirect = True;
  4460.     atts.save_under = True;
  4461.     } else {
  4462.     atts.override_redirect = False;
  4463.     atts.save_under = False;
  4464.     }
  4465.     
  4466.     if ((atts.override_redirect != Tk_Attributes(wrapperPtr)->override_redirect)
  4467.         || (atts.save_under != Tk_Attributes(wrapperPtr)->save_under)) {
  4468.     Tk_ChangeWindowAttributes((Tk_Window) wrapperPtr,
  4469.         CWOverrideRedirect|CWSaveUnder, &atts);
  4470.     }
  4471. }
  4472.  
  4473. /*
  4474.  *----------------------------------------------------------------------
  4475.  *
  4476.  * CreateWrapper --
  4477.  *
  4478.  *    This procedure is invoked to create the wrapper window for a
  4479.  *    toplevel window.  It is called just before a toplevel is mapped
  4480.  *    for the first time.
  4481.  *
  4482.  * Results:
  4483.  *    None.
  4484.  *
  4485.  * Side effects:
  4486.  *    The wrapper is created and the toplevel is reparented inside it.
  4487.  *
  4488.  *----------------------------------------------------------------------
  4489.  */
  4490.  
  4491. static void
  4492. CreateWrapper(wmPtr)
  4493.     WmInfo *wmPtr;        /* Window manager information for the
  4494.                  * window. */
  4495. {
  4496.     TkWindow *winPtr, *wrapperPtr;
  4497.     Window parent;
  4498.     Tcl_HashEntry *hPtr;
  4499.     int new;
  4500.  
  4501.     winPtr = wmPtr->winPtr;
  4502.     if (winPtr->window == None) {
  4503.     Tk_MakeWindowExist((Tk_Window) winPtr);
  4504.     }
  4505.  
  4506.     /*
  4507.      * The code below is copied from CreateTopLevelWindow,
  4508.      * Tk_MakeWindowExist, and TkpMakeWindow; The idea is to create an
  4509.      * "official" Tk window (so that we can get events on it), but to
  4510.      * hide the window outside the official Tk hierarchy so that it
  4511.      * isn't visible to the application.  See the comments for the other
  4512.      * procedures if you have questions about this code.
  4513.      */
  4514.  
  4515.     wmPtr->wrapperPtr = wrapperPtr = TkAllocWindow(winPtr->dispPtr,
  4516.         Tk_ScreenNumber((Tk_Window) winPtr), winPtr);
  4517.     wrapperPtr->dirtyAtts |= CWBorderPixel;
  4518.  
  4519.     /*
  4520.      * Tk doesn't normally select for StructureNotifyMask events because
  4521.      * the events are synthesized internally.  However, for wrapper
  4522.      * windows we need to know when the window manager modifies the
  4523.      * window configuration.  We also need to select on focus change
  4524.      * events; these are the only windows for which we care about focus
  4525.      * changes.
  4526.      */
  4527.  
  4528.     wrapperPtr->flags |= TK_WRAPPER;
  4529.     wrapperPtr->atts.event_mask |= StructureNotifyMask|FocusChangeMask;
  4530.     wrapperPtr->atts.override_redirect = winPtr->atts.override_redirect;
  4531.     if (winPtr->flags & TK_EMBEDDED) {
  4532.     parent = TkUnixContainerId(winPtr);
  4533.     } else {
  4534.     parent = XRootWindow(wrapperPtr->display, wrapperPtr->screenNum);
  4535.     }
  4536.     wrapperPtr->window = XCreateWindow(wrapperPtr->display,
  4537.         parent, wrapperPtr->changes.x, wrapperPtr->changes.y,
  4538.         (unsigned) wrapperPtr->changes.width,
  4539.         (unsigned) wrapperPtr->changes.height,
  4540.         (unsigned) wrapperPtr->changes.border_width, wrapperPtr->depth,
  4541.         InputOutput, wrapperPtr->visual,
  4542.         wrapperPtr->dirtyAtts|CWOverrideRedirect, &wrapperPtr->atts);
  4543.     hPtr = Tcl_CreateHashEntry(&wrapperPtr->dispPtr->winTable,
  4544.         (char *) wrapperPtr->window, &new);
  4545.     Tcl_SetHashValue(hPtr, wrapperPtr);
  4546.     wrapperPtr->mainPtr = winPtr->mainPtr;
  4547.     wrapperPtr->mainPtr->refCount++;
  4548.     wrapperPtr->dirtyAtts = 0;
  4549.     wrapperPtr->dirtyChanges = 0;
  4550. #ifdef TK_USE_INPUT_METHODS
  4551.     wrapperPtr->inputContext = NULL;
  4552. #endif /* TK_USE_INPUT_METHODS */
  4553.     wrapperPtr->wmInfoPtr = wmPtr;
  4554.  
  4555.     /*
  4556.      * Reparent the toplevel window inside the wrapper.
  4557.      */
  4558.  
  4559.     XReparentWindow(wrapperPtr->display, winPtr->window, wrapperPtr->window,
  4560.         0, 0);
  4561.  
  4562.     /*
  4563.      * Tk must monitor structure events for wrapper windows in order
  4564.      * to detect changes made by window managers such as resizing,
  4565.      * mapping, unmapping, etc..
  4566.      */
  4567.  
  4568.     Tk_CreateEventHandler((Tk_Window) wmPtr->wrapperPtr, StructureNotifyMask,
  4569.         WrapperEventProc, (ClientData) wmPtr);
  4570. }
  4571.  
  4572. /*
  4573.  *----------------------------------------------------------------------
  4574.  *
  4575.  * TkWmFocusToplevel --
  4576.  *
  4577.  *    This is a utility procedure invoked by focus-management code.
  4578.  *    The focus code responds to externally generated focus-related
  4579.  *    events on wrapper windows but ignores those events for any other
  4580.  *    windows.  This procedure determines whether a given window is a
  4581.  *    wrapper window and, if so, returns the toplevel window
  4582.  *    corresponding to the wrapper.
  4583.  *
  4584.  * Results:
  4585.  *    If winPtr is a wrapper window, returns a pointer to the
  4586.  *    corresponding toplevel window; otherwise returns NULL.
  4587.  *
  4588.  * Side effects:
  4589.  *    None.
  4590.  *
  4591.  *----------------------------------------------------------------------
  4592.  */
  4593.  
  4594. TkWindow *
  4595. TkWmFocusToplevel(winPtr)
  4596.     TkWindow *winPtr;        /* Window that received a focus-related
  4597.                  * event. */
  4598. {
  4599.     if (!(winPtr->flags & TK_WRAPPER)) {
  4600.     return NULL;
  4601.     }
  4602.     return winPtr->wmInfoPtr->winPtr;
  4603. }
  4604.  
  4605. /*
  4606.  *----------------------------------------------------------------------
  4607.  *
  4608.  * TkUnixSetMenubar --
  4609.  *
  4610.  *    This procedure is invoked by menu management code to specify the
  4611.  *    window to use as a menubar for a given toplevel window.
  4612.  *
  4613.  * Results:
  4614.  *    None.
  4615.  *
  4616.  * Side effects:
  4617.  *    The window given by menubar will be mapped and positioned inside
  4618.  *    the wrapper for tkwin and above tkwin.  Menubar will
  4619.  *    automatically be resized to maintain the height specified by
  4620.  *    TkUnixSetMenuHeight the same width as tkwin.  Any previous
  4621.  *    menubar specified for tkwin will be unmapped and ignored from
  4622.  *    now on.
  4623.  *
  4624.  *----------------------------------------------------------------------
  4625.  */
  4626.  
  4627. void
  4628. TkUnixSetMenubar(tkwin, menubar)
  4629.     Tk_Window tkwin;        /* Token for toplevel window. */
  4630.     Tk_Window menubar;        /* Token for window that is to serve as
  4631.                  * menubar for tkwin.  Must not be a
  4632.                  * toplevel window.  If NULL, any
  4633.                  * existing menubar is canceled and the
  4634.                  * menu height is reset to 0. */
  4635. {
  4636.     WmInfo *wmPtr = ((TkWindow *) tkwin)->wmInfoPtr;
  4637.     Tk_Window parent;
  4638.     TkWindow *menubarPtr = (TkWindow *) menubar;
  4639.  
  4640.     if (wmPtr->menubar != NULL) {
  4641.     /*
  4642.      * There's already a menubar for this toplevel.  If it isn't the
  4643.      * same as the new menubar, unmap it so that it is out of the
  4644.      * way, and reparent it back to its original parent.
  4645.      */
  4646.  
  4647.     if (wmPtr->menubar == menubar) {
  4648.         return;
  4649.     }
  4650.     ((TkWindow *) wmPtr->menubar)->wmInfoPtr = NULL;
  4651.     ((TkWindow *) wmPtr->menubar)->flags &= ~TK_REPARENTED;
  4652.     Tk_UnmapWindow(wmPtr->menubar);
  4653.     parent = Tk_Parent(wmPtr->menubar);
  4654.     if (parent != NULL) {
  4655.         Tk_MakeWindowExist(parent);
  4656.         XReparentWindow(Tk_Display(wmPtr->menubar),
  4657.             Tk_WindowId(wmPtr->menubar), Tk_WindowId(parent), 0, 0);
  4658.     }
  4659.     Tk_DeleteEventHandler(wmPtr->menubar, StructureNotifyMask,
  4660.         MenubarDestroyProc, (ClientData) wmPtr->menubar);
  4661.     Tk_ManageGeometry(wmPtr->menubar, NULL, (ClientData) NULL);
  4662.     }
  4663.  
  4664.     wmPtr->menubar = menubar;
  4665.     if (menubar == NULL) {
  4666.     wmPtr->menuHeight = 0;
  4667.     } else {
  4668.     if ((menubarPtr->flags & TK_TOP_LEVEL)
  4669.          || (Tk_Screen(menubar) != Tk_Screen(tkwin))) {
  4670.         panic("TkUnixSetMenubar got bad menubar");
  4671.     }
  4672.     wmPtr->menuHeight = Tk_ReqHeight(menubar);
  4673.     if (wmPtr->menuHeight == 0) {
  4674.         wmPtr->menuHeight = 1;
  4675.     }
  4676.     Tk_MakeWindowExist(tkwin);
  4677.     Tk_MakeWindowExist(menubar);
  4678.     if (wmPtr->wrapperPtr == NULL) {
  4679.         CreateWrapper(wmPtr);
  4680.     }
  4681.     XReparentWindow(Tk_Display(menubar), Tk_WindowId(menubar),
  4682.         wmPtr->wrapperPtr->window, 0, 0);
  4683.     menubarPtr->wmInfoPtr = wmPtr;
  4684.     Tk_MoveResizeWindow(menubar, 0, 0, Tk_Width(tkwin), wmPtr->menuHeight);
  4685.     Tk_MapWindow(menubar);
  4686.     Tk_CreateEventHandler(menubar, StructureNotifyMask, MenubarDestroyProc,
  4687.         (ClientData) menubar);
  4688.     Tk_ManageGeometry(menubar, &menubarMgrType, (ClientData) wmPtr);
  4689.     menubarPtr->flags |= TK_REPARENTED;
  4690.     }
  4691.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  4692.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  4693.     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) tkwin);
  4694.     wmPtr->flags |= WM_UPDATE_PENDING;
  4695.     }
  4696. }
  4697.  
  4698. /*
  4699.  *----------------------------------------------------------------------
  4700.  *
  4701.  * MenubarDestroyProc --
  4702.  *
  4703.  *    This procedure is invoked by the event dispatcher whenever a
  4704.  *    menubar window is destroyed (it's also invoked for a few other
  4705.  *    kinds of events, but we ignore those).
  4706.  *
  4707.  * Results:
  4708.  *    None.
  4709.  *
  4710.  * Side effects:
  4711.  *    The association between the window and its toplevel is broken,
  4712.  *    so that the window is no longer considered to be a menubar.
  4713.  *
  4714.  *----------------------------------------------------------------------
  4715.  */
  4716.  
  4717. static void
  4718. MenubarDestroyProc(clientData, eventPtr)
  4719.     ClientData clientData;        /* TkWindow pointer for menubar. */
  4720.     XEvent *eventPtr;            /* Describes what just happened. */
  4721. {
  4722.     WmInfo *wmPtr;
  4723.  
  4724.     if (eventPtr->type != DestroyNotify) {
  4725.     return;
  4726.     }
  4727.     wmPtr = ((TkWindow *) clientData)->wmInfoPtr;
  4728.     wmPtr->menubar = NULL;
  4729.     wmPtr->menuHeight = 0;
  4730.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  4731.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  4732.     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) wmPtr->winPtr);
  4733.     wmPtr->flags |= WM_UPDATE_PENDING;
  4734.     }
  4735. }
  4736.  
  4737. /*
  4738.  *----------------------------------------------------------------------
  4739.  *
  4740.  * MenubarReqProc --
  4741.  *
  4742.  *    This procedure is invoked by the Tk geometry management code
  4743.  *    whenever a menubar calls Tk_GeometryRequest to request a new
  4744.  *    size.
  4745.  *
  4746.  * Results:
  4747.  *    None.
  4748.  *
  4749.  * Side effects:
  4750.  *    None.
  4751.  *
  4752.  *----------------------------------------------------------------------
  4753.  */
  4754.  
  4755. static void
  4756. MenubarReqProc(clientData, tkwin)
  4757.     ClientData clientData;    /* Pointer to the window manager
  4758.                  * information for tkwin's toplevel. */
  4759.     Tk_Window tkwin;        /* Handle for menubar window. */
  4760. {
  4761.     WmInfo *wmPtr = (WmInfo *) clientData;
  4762.  
  4763.     wmPtr->menuHeight = Tk_ReqHeight(tkwin);
  4764.     if (wmPtr->menuHeight <= 0) {
  4765.     wmPtr->menuHeight = 1;
  4766.     }
  4767.     wmPtr->flags |= WM_UPDATE_SIZE_HINTS;
  4768.     if (!(wmPtr->flags & (WM_UPDATE_PENDING|WM_NEVER_MAPPED))) {
  4769.     Tcl_DoWhenIdle(UpdateGeometryInfo, (ClientData) wmPtr->winPtr);
  4770.     wmPtr->flags |= WM_UPDATE_PENDING;
  4771.     }
  4772. }
  4773.  
  4774. /*
  4775.  *----------------------------------------------------------------------
  4776.  *
  4777.  * TkpGetWrapperWindow --
  4778.  *
  4779.  *    Given a toplevel window return the hidden wrapper window for
  4780.  *    the toplevel window if available.
  4781.  *
  4782.  * Results:
  4783.  *    The wrapper window.  NULL is we were not passed a toplevel
  4784.  *    window or the wrapper has yet to be created.
  4785.  *
  4786.  * Side effects:
  4787.  *    None.
  4788.  *
  4789.  *----------------------------------------------------------------------
  4790.  */
  4791.  
  4792. TkWindow *
  4793. TkpGetWrapperWindow(winPtr)
  4794.     TkWindow *winPtr;        /* A toplevel window pointer. */
  4795. {
  4796.     register WmInfo *wmPtr = winPtr->wmInfoPtr;
  4797.  
  4798.     if ((winPtr == NULL) || (wmPtr == NULL)) {
  4799.     return NULL;
  4800.     }
  4801.  
  4802.     return wmPtr->wrapperPtr;
  4803. }
  4804.